@float.js/core 2.0.6 → 2.1.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 +118 -0
- package/dist/cli/index.js +358 -114
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +368 -124
- package/dist/index.js.map +1 -1
- package/dist/server/index.js +296 -52
- package/dist/server/index.js.map +1 -1
- package/package.json +14 -3
package/dist/cli/index.js
CHANGED
|
@@ -730,7 +730,7 @@ function generateAPIResponse(state) {
|
|
|
730
730
|
function createDevDashboard(options = {}) {
|
|
731
731
|
const {
|
|
732
732
|
enabled = process.env.NODE_ENV !== "production",
|
|
733
|
-
path:
|
|
733
|
+
path: path9 = "/__float",
|
|
734
734
|
auth
|
|
735
735
|
} = options;
|
|
736
736
|
if (!enabled) {
|
|
@@ -738,7 +738,7 @@ function createDevDashboard(options = {}) {
|
|
|
738
738
|
}
|
|
739
739
|
return (req, res, next) => {
|
|
740
740
|
const url = req.url || "";
|
|
741
|
-
if (!url.startsWith(
|
|
741
|
+
if (!url.startsWith(path9)) {
|
|
742
742
|
return next();
|
|
743
743
|
}
|
|
744
744
|
if (auth) {
|
|
@@ -757,7 +757,7 @@ function createDevDashboard(options = {}) {
|
|
|
757
757
|
return;
|
|
758
758
|
}
|
|
759
759
|
}
|
|
760
|
-
const subPath = url.slice(
|
|
760
|
+
const subPath = url.slice(path9.length);
|
|
761
761
|
if (subPath === "" || subPath === "/") {
|
|
762
762
|
res.setHeader("Content-Type", "text/html");
|
|
763
763
|
res.end(generateDashboardHTML(dashboardState));
|
|
@@ -868,14 +868,14 @@ var init_devtools = __esm({
|
|
|
868
868
|
// src/cli/index.ts
|
|
869
869
|
init_esm_shims();
|
|
870
870
|
import { cac } from "cac";
|
|
871
|
-
import
|
|
871
|
+
import pc5 from "picocolors";
|
|
872
872
|
|
|
873
873
|
// src/server/dev-server.ts
|
|
874
874
|
init_esm_shims();
|
|
875
875
|
import http from "http";
|
|
876
|
-
import
|
|
877
|
-
import
|
|
878
|
-
import
|
|
876
|
+
import fs4 from "fs";
|
|
877
|
+
import path6 from "path";
|
|
878
|
+
import pc2 from "picocolors";
|
|
879
879
|
import chokidar from "chokidar";
|
|
880
880
|
import { WebSocketServer, WebSocket } from "ws";
|
|
881
881
|
import mime from "mime-types";
|
|
@@ -2464,11 +2464,214 @@ function generateExamplesPage() {
|
|
|
2464
2464
|
` + footer;
|
|
2465
2465
|
}
|
|
2466
2466
|
|
|
2467
|
+
// src/build/css-processor.ts
|
|
2468
|
+
init_esm_shims();
|
|
2469
|
+
import fs3 from "fs";
|
|
2470
|
+
import path5 from "path";
|
|
2471
|
+
|
|
2472
|
+
// src/build/tailwind-setup.ts
|
|
2473
|
+
init_esm_shims();
|
|
2474
|
+
import fs2 from "fs";
|
|
2475
|
+
import path4 from "path";
|
|
2476
|
+
import pc from "picocolors";
|
|
2477
|
+
function checkTailwindSetup(rootDir) {
|
|
2478
|
+
const possibleConfigs = [
|
|
2479
|
+
"tailwind.config.js",
|
|
2480
|
+
"tailwind.config.ts",
|
|
2481
|
+
"tailwind.config.mjs",
|
|
2482
|
+
"tailwind.config.cjs"
|
|
2483
|
+
];
|
|
2484
|
+
let configPath = null;
|
|
2485
|
+
for (const config of possibleConfigs) {
|
|
2486
|
+
const fullPath = path4.join(rootDir, config);
|
|
2487
|
+
if (fs2.existsSync(fullPath)) {
|
|
2488
|
+
configPath = fullPath;
|
|
2489
|
+
break;
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
const globalsPath = path4.join(rootDir, "app", "globals.css");
|
|
2493
|
+
const hasGlobals = fs2.existsSync(globalsPath);
|
|
2494
|
+
return {
|
|
2495
|
+
hasTailwind: !!configPath,
|
|
2496
|
+
configPath,
|
|
2497
|
+
globalsPath: hasGlobals ? globalsPath : null,
|
|
2498
|
+
needsSetup: !configPath || !hasGlobals
|
|
2499
|
+
};
|
|
2500
|
+
}
|
|
2501
|
+
async function setupTailwind(rootDir, options = {}) {
|
|
2502
|
+
const { force = false, silent = false } = options;
|
|
2503
|
+
const config = checkTailwindSetup(rootDir);
|
|
2504
|
+
if (!force && !config.needsSetup) {
|
|
2505
|
+
return;
|
|
2506
|
+
}
|
|
2507
|
+
if (!silent) {
|
|
2508
|
+
console.log(pc.cyan("\n\u{1F3A8} Setting up Tailwind CSS..."));
|
|
2509
|
+
}
|
|
2510
|
+
if (!config.configPath || force) {
|
|
2511
|
+
const tailwindConfig = `/** @type {import('tailwindcss').Config} */
|
|
2512
|
+
export default {
|
|
2513
|
+
content: [
|
|
2514
|
+
'./app/**/*.{js,ts,jsx,tsx}',
|
|
2515
|
+
'./components/**/*.{js,ts,jsx,tsx}',
|
|
2516
|
+
],
|
|
2517
|
+
theme: {
|
|
2518
|
+
extend: {},
|
|
2519
|
+
},
|
|
2520
|
+
plugins: [],
|
|
2521
|
+
}
|
|
2522
|
+
`;
|
|
2523
|
+
const configPath = path4.join(rootDir, "tailwind.config.js");
|
|
2524
|
+
fs2.writeFileSync(configPath, tailwindConfig);
|
|
2525
|
+
if (!silent) {
|
|
2526
|
+
console.log(pc.green(" \u2713 Created tailwind.config.js"));
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
const postcssPath = path4.join(rootDir, "postcss.config.js");
|
|
2530
|
+
if (!fs2.existsSync(postcssPath) || force) {
|
|
2531
|
+
const postcssConfig = `export default {
|
|
2532
|
+
plugins: {
|
|
2533
|
+
tailwindcss: {},
|
|
2534
|
+
autoprefixer: {},
|
|
2535
|
+
},
|
|
2536
|
+
}
|
|
2537
|
+
`;
|
|
2538
|
+
fs2.writeFileSync(postcssPath, postcssConfig);
|
|
2539
|
+
if (!silent) {
|
|
2540
|
+
console.log(pc.green(" \u2713 Created postcss.config.js"));
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
const appDir = path4.join(rootDir, "app");
|
|
2544
|
+
if (!fs2.existsSync(appDir)) {
|
|
2545
|
+
fs2.mkdirSync(appDir, { recursive: true });
|
|
2546
|
+
}
|
|
2547
|
+
const globalsPath = path4.join(appDir, "globals.css");
|
|
2548
|
+
if (!fs2.existsSync(globalsPath) || force) {
|
|
2549
|
+
const globalsCss = `@tailwind base;
|
|
2550
|
+
@tailwind components;
|
|
2551
|
+
@tailwind utilities;
|
|
2552
|
+
`;
|
|
2553
|
+
fs2.writeFileSync(globalsPath, globalsCss);
|
|
2554
|
+
if (!silent) {
|
|
2555
|
+
console.log(pc.green(" \u2713 Created app/globals.css"));
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
const layoutPath = path4.join(appDir, "layout.tsx");
|
|
2559
|
+
if (!fs2.existsSync(layoutPath)) {
|
|
2560
|
+
const layoutContent = `import './globals.css'
|
|
2561
|
+
|
|
2562
|
+
export default function RootLayout({
|
|
2563
|
+
children,
|
|
2564
|
+
}: {
|
|
2565
|
+
children: React.ReactNode
|
|
2566
|
+
}) {
|
|
2567
|
+
return (
|
|
2568
|
+
<html lang="en">
|
|
2569
|
+
<body>{children}</body>
|
|
2570
|
+
</html>
|
|
2571
|
+
)
|
|
2572
|
+
}
|
|
2573
|
+
`;
|
|
2574
|
+
fs2.writeFileSync(layoutPath, layoutContent);
|
|
2575
|
+
if (!silent) {
|
|
2576
|
+
console.log(pc.green(" \u2713 Created app/layout.tsx"));
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
if (!silent) {
|
|
2580
|
+
console.log(pc.green("\n\u2728 Tailwind CSS ready!\n"));
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
function checkTailwindDeps(rootDir) {
|
|
2584
|
+
const packageJsonPath = path4.join(rootDir, "package.json");
|
|
2585
|
+
if (!fs2.existsSync(packageJsonPath)) {
|
|
2586
|
+
return {
|
|
2587
|
+
hasPackageJson: false,
|
|
2588
|
+
hasTailwind: false,
|
|
2589
|
+
hasPostCSS: false,
|
|
2590
|
+
hasAutoprefixer: false
|
|
2591
|
+
};
|
|
2592
|
+
}
|
|
2593
|
+
const packageJson = JSON.parse(fs2.readFileSync(packageJsonPath, "utf-8"));
|
|
2594
|
+
const allDeps = {
|
|
2595
|
+
...packageJson.dependencies,
|
|
2596
|
+
...packageJson.devDependencies
|
|
2597
|
+
};
|
|
2598
|
+
return {
|
|
2599
|
+
hasPackageJson: true,
|
|
2600
|
+
hasTailwind: !!allDeps["tailwindcss"],
|
|
2601
|
+
hasPostCSS: !!allDeps["postcss"],
|
|
2602
|
+
hasAutoprefixer: !!allDeps["autoprefixer"]
|
|
2603
|
+
};
|
|
2604
|
+
}
|
|
2605
|
+
function getTailwindInstallCommand(rootDir) {
|
|
2606
|
+
const deps = checkTailwindDeps(rootDir);
|
|
2607
|
+
if (!deps.hasPackageJson) {
|
|
2608
|
+
return null;
|
|
2609
|
+
}
|
|
2610
|
+
const missing = [];
|
|
2611
|
+
if (!deps.hasTailwind) missing.push("tailwindcss");
|
|
2612
|
+
if (!deps.hasPostCSS) missing.push("postcss");
|
|
2613
|
+
if (!deps.hasAutoprefixer) missing.push("autoprefixer");
|
|
2614
|
+
if (missing.length === 0) {
|
|
2615
|
+
return null;
|
|
2616
|
+
}
|
|
2617
|
+
const hasYarnLock = fs2.existsSync(path4.join(rootDir, "yarn.lock"));
|
|
2618
|
+
const hasPnpmLock = fs2.existsSync(path4.join(rootDir, "pnpm-lock.yaml"));
|
|
2619
|
+
const hasBunLock = fs2.existsSync(path4.join(rootDir, "bun.lockb"));
|
|
2620
|
+
let pm = "npm install -D";
|
|
2621
|
+
if (hasBunLock) pm = "bun add -d";
|
|
2622
|
+
else if (hasPnpmLock) pm = "pnpm add -D";
|
|
2623
|
+
else if (hasYarnLock) pm = "yarn add -D";
|
|
2624
|
+
return `${pm} ${missing.join(" ")}`;
|
|
2625
|
+
}
|
|
2626
|
+
|
|
2627
|
+
// src/build/css-processor.ts
|
|
2628
|
+
async function processCSS(filePath, rootDir = process.cwd()) {
|
|
2629
|
+
const content = fs3.readFileSync(filePath, "utf-8");
|
|
2630
|
+
const tailwindConfig = checkTailwindSetup(rootDir);
|
|
2631
|
+
if (!tailwindConfig.hasTailwind) {
|
|
2632
|
+
return { code: content };
|
|
2633
|
+
}
|
|
2634
|
+
try {
|
|
2635
|
+
const postcss = await import("postcss").then((m) => m.default);
|
|
2636
|
+
const tailwindcss = await import("tailwindcss").then((m) => m.default);
|
|
2637
|
+
const autoprefixer = await import("autoprefixer").then((m) => m.default);
|
|
2638
|
+
const configPath = tailwindConfig.configPath || path5.join(rootDir, "tailwind.config.js");
|
|
2639
|
+
let tailwindConfigModule = {};
|
|
2640
|
+
if (fs3.existsSync(configPath)) {
|
|
2641
|
+
const configUrl = new URL(`file://${configPath}`);
|
|
2642
|
+
tailwindConfigModule = await import(configUrl.href).then((m) => m.default || m);
|
|
2643
|
+
}
|
|
2644
|
+
const result = await postcss([
|
|
2645
|
+
tailwindcss(tailwindConfigModule),
|
|
2646
|
+
autoprefixer()
|
|
2647
|
+
]).process(content, {
|
|
2648
|
+
from: filePath,
|
|
2649
|
+
to: filePath,
|
|
2650
|
+
map: { inline: false }
|
|
2651
|
+
});
|
|
2652
|
+
return {
|
|
2653
|
+
code: result.css,
|
|
2654
|
+
map: result.map?.toString()
|
|
2655
|
+
};
|
|
2656
|
+
} catch (error) {
|
|
2657
|
+
console.warn("CSS processing failed, serving raw CSS:", error);
|
|
2658
|
+
return { code: content };
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2661
|
+
function needsCSSProcessing(filePath, rootDir) {
|
|
2662
|
+
const config = checkTailwindSetup(rootDir);
|
|
2663
|
+
if (!config.hasTailwind) {
|
|
2664
|
+
return false;
|
|
2665
|
+
}
|
|
2666
|
+
const content = fs3.readFileSync(filePath, "utf-8");
|
|
2667
|
+
return content.includes("@tailwind") || content.includes("@apply");
|
|
2668
|
+
}
|
|
2669
|
+
|
|
2467
2670
|
// src/server/dev-server.ts
|
|
2468
2671
|
async function createDevServer(options) {
|
|
2469
2672
|
const { port, host, open } = options;
|
|
2470
2673
|
const rootDir = process.cwd();
|
|
2471
|
-
const publicDir =
|
|
2674
|
+
const publicDir = path6.join(rootDir, "public");
|
|
2472
2675
|
let routes = [];
|
|
2473
2676
|
let server = null;
|
|
2474
2677
|
let wss = null;
|
|
@@ -2476,9 +2679,20 @@ async function createDevServer(options) {
|
|
|
2476
2679
|
async function refreshRoutes() {
|
|
2477
2680
|
try {
|
|
2478
2681
|
routes = await scanRoutes(rootDir);
|
|
2479
|
-
console.log(
|
|
2682
|
+
console.log(pc2.dim(` \u{1F4C1} Found ${routes.length} routes`));
|
|
2683
|
+
const tailwindConfig = checkTailwindSetup(rootDir);
|
|
2684
|
+
if (tailwindConfig.needsSetup) {
|
|
2685
|
+
console.log(pc2.yellow(" \u26A0\uFE0F Tailwind not configured"));
|
|
2686
|
+
const installCmd = getTailwindInstallCommand(rootDir);
|
|
2687
|
+
if (installCmd) {
|
|
2688
|
+
console.log(pc2.dim(` \u{1F4A1} Run: ${installCmd}`));
|
|
2689
|
+
console.log(pc2.dim(` \u{1F4A1} Then: npx float dev`));
|
|
2690
|
+
} else {
|
|
2691
|
+
await setupTailwind(rootDir);
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2480
2694
|
} catch (error) {
|
|
2481
|
-
console.error(
|
|
2695
|
+
console.error(pc2.red("Failed to scan routes:"), error);
|
|
2482
2696
|
}
|
|
2483
2697
|
}
|
|
2484
2698
|
function notifyClients(type, data) {
|
|
@@ -2539,16 +2753,46 @@ ${FLOAT_ERROR_OVERLAY}
|
|
|
2539
2753
|
async function handleRequest(req, res) {
|
|
2540
2754
|
const url = new URL(req.url || "/", `http://${host}:${port}`);
|
|
2541
2755
|
const pathname = url.pathname;
|
|
2542
|
-
console.log(
|
|
2756
|
+
console.log(pc2.dim(` ${req.method} ${pathname}`));
|
|
2543
2757
|
try {
|
|
2544
|
-
const publicFilePath =
|
|
2545
|
-
if (
|
|
2546
|
-
const content =
|
|
2758
|
+
const publicFilePath = path6.join(publicDir, pathname);
|
|
2759
|
+
if (fs4.existsSync(publicFilePath) && fs4.statSync(publicFilePath).isFile()) {
|
|
2760
|
+
const content = fs4.readFileSync(publicFilePath);
|
|
2547
2761
|
const contentType = mime.lookup(publicFilePath) || "application/octet-stream";
|
|
2548
2762
|
res.writeHead(200, { "Content-Type": contentType });
|
|
2549
2763
|
res.end(content);
|
|
2550
2764
|
return;
|
|
2551
2765
|
}
|
|
2766
|
+
if (pathname.endsWith(".css")) {
|
|
2767
|
+
const cssPath = path6.join(rootDir, "app", pathname.replace(/^\//, ""));
|
|
2768
|
+
if (fs4.existsSync(cssPath)) {
|
|
2769
|
+
try {
|
|
2770
|
+
const needsProcessing = needsCSSProcessing(cssPath, rootDir);
|
|
2771
|
+
if (needsProcessing) {
|
|
2772
|
+
const result = await processCSS(cssPath, rootDir);
|
|
2773
|
+
res.writeHead(200, {
|
|
2774
|
+
"Content-Type": "text/css",
|
|
2775
|
+
"Cache-Control": "no-cache"
|
|
2776
|
+
});
|
|
2777
|
+
res.end(result.code);
|
|
2778
|
+
} else {
|
|
2779
|
+
const content = fs4.readFileSync(cssPath, "utf-8");
|
|
2780
|
+
res.writeHead(200, {
|
|
2781
|
+
"Content-Type": "text/css",
|
|
2782
|
+
"Cache-Control": "no-cache"
|
|
2783
|
+
});
|
|
2784
|
+
res.end(content);
|
|
2785
|
+
}
|
|
2786
|
+
return;
|
|
2787
|
+
} catch (error) {
|
|
2788
|
+
console.error(pc2.red("CSS processing error:"), error);
|
|
2789
|
+
const content = fs4.readFileSync(cssPath, "utf-8");
|
|
2790
|
+
res.writeHead(200, { "Content-Type": "text/css" });
|
|
2791
|
+
res.end(content);
|
|
2792
|
+
return;
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2552
2796
|
if (pathname.startsWith("/_float/")) {
|
|
2553
2797
|
res.writeHead(200, { "Content-Type": "application/javascript" });
|
|
2554
2798
|
res.end("// Float.js internal asset");
|
|
@@ -2613,7 +2857,7 @@ ${FLOAT_ERROR_OVERLAY}
|
|
|
2613
2857
|
});
|
|
2614
2858
|
res.end(html);
|
|
2615
2859
|
} catch (error) {
|
|
2616
|
-
console.error(
|
|
2860
|
+
console.error(pc2.red("Request error:"), error);
|
|
2617
2861
|
res.writeHead(500, { "Content-Type": "text/html" });
|
|
2618
2862
|
res.end(createErrorPage(error));
|
|
2619
2863
|
}
|
|
@@ -2642,7 +2886,7 @@ ${FLOAT_ERROR_OVERLAY}
|
|
|
2642
2886
|
const responseBody = await response.text();
|
|
2643
2887
|
res.end(responseBody);
|
|
2644
2888
|
} catch (error) {
|
|
2645
|
-
console.error(
|
|
2889
|
+
console.error(pc2.red("API route error:"), error);
|
|
2646
2890
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
2647
2891
|
res.end(JSON.stringify({ error: "Internal server error" }));
|
|
2648
2892
|
}
|
|
@@ -2657,9 +2901,9 @@ ${FLOAT_ERROR_OVERLAY}
|
|
|
2657
2901
|
});
|
|
2658
2902
|
const watcher = chokidar.watch(
|
|
2659
2903
|
[
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2904
|
+
path6.join(rootDir, "app/**/*.{ts,tsx,js,jsx}"),
|
|
2905
|
+
path6.join(rootDir, "components/**/*.{ts,tsx,js,jsx}"),
|
|
2906
|
+
path6.join(rootDir, "lib/**/*.{ts,tsx,js,jsx}")
|
|
2663
2907
|
],
|
|
2664
2908
|
{
|
|
2665
2909
|
ignored: /node_modules/,
|
|
@@ -2667,8 +2911,8 @@ ${FLOAT_ERROR_OVERLAY}
|
|
|
2667
2911
|
}
|
|
2668
2912
|
);
|
|
2669
2913
|
watcher.on("change", async (filePath) => {
|
|
2670
|
-
console.log(
|
|
2671
|
-
\u26A1 File changed: ${
|
|
2914
|
+
console.log(pc2.yellow(`
|
|
2915
|
+
\u26A1 File changed: ${path6.relative(rootDir, filePath)}`));
|
|
2672
2916
|
if (filePath.includes("/app/")) {
|
|
2673
2917
|
await refreshRoutes();
|
|
2674
2918
|
}
|
|
@@ -2676,31 +2920,31 @@ ${FLOAT_ERROR_OVERLAY}
|
|
|
2676
2920
|
});
|
|
2677
2921
|
watcher.on("add", async (filePath) => {
|
|
2678
2922
|
if (filePath.includes("/app/")) {
|
|
2679
|
-
console.log(
|
|
2680
|
-
\u2795 File added: ${
|
|
2923
|
+
console.log(pc2.green(`
|
|
2924
|
+
\u2795 File added: ${path6.relative(rootDir, filePath)}`));
|
|
2681
2925
|
await refreshRoutes();
|
|
2682
2926
|
notifyClients("reload");
|
|
2683
2927
|
}
|
|
2684
2928
|
});
|
|
2685
2929
|
watcher.on("unlink", async (filePath) => {
|
|
2686
2930
|
if (filePath.includes("/app/")) {
|
|
2687
|
-
console.log(
|
|
2688
|
-
\u2796 File removed: ${
|
|
2931
|
+
console.log(pc2.red(`
|
|
2932
|
+
\u2796 File removed: ${path6.relative(rootDir, filePath)}`));
|
|
2689
2933
|
await refreshRoutes();
|
|
2690
2934
|
notifyClients("reload");
|
|
2691
2935
|
}
|
|
2692
2936
|
});
|
|
2693
2937
|
return new Promise((resolve, reject) => {
|
|
2694
2938
|
server.listen(port, host, () => {
|
|
2695
|
-
console.log(
|
|
2696
|
-
console.log(
|
|
2939
|
+
console.log(pc2.green(` \u2705 Server running at ${pc2.cyan(`http://${host}:${port}`)}`));
|
|
2940
|
+
console.log(pc2.dim(` \u26A1 HMR enabled on ws://${host}:${port + 1}
|
|
2697
2941
|
`));
|
|
2698
|
-
console.log(
|
|
2942
|
+
console.log(pc2.bold(" Routes:"));
|
|
2699
2943
|
routes.forEach((route) => {
|
|
2700
2944
|
if (route.type === "page") {
|
|
2701
|
-
console.log(
|
|
2945
|
+
console.log(pc2.dim(` ${pc2.green("\u25CF")} ${route.path}`));
|
|
2702
2946
|
} else if (route.type === "api") {
|
|
2703
|
-
console.log(
|
|
2947
|
+
console.log(pc2.dim(` ${pc2.blue("\u25C6")} ${route.path} (API)`));
|
|
2704
2948
|
}
|
|
2705
2949
|
});
|
|
2706
2950
|
console.log("");
|
|
@@ -2820,9 +3064,9 @@ function escapeHtml2(text) {
|
|
|
2820
3064
|
// src/build/index.ts
|
|
2821
3065
|
init_esm_shims();
|
|
2822
3066
|
import * as esbuild2 from "esbuild";
|
|
2823
|
-
import
|
|
2824
|
-
import
|
|
2825
|
-
import
|
|
3067
|
+
import fs5 from "fs";
|
|
3068
|
+
import path7 from "path";
|
|
3069
|
+
import pc3 from "picocolors";
|
|
2826
3070
|
var DEFAULT_BUILD_OPTIONS = {
|
|
2827
3071
|
analyze: false,
|
|
2828
3072
|
minify: true,
|
|
@@ -2832,27 +3076,27 @@ async function build2(options = {}) {
|
|
|
2832
3076
|
const opts = { ...DEFAULT_BUILD_OPTIONS, ...options };
|
|
2833
3077
|
const startTime = Date.now();
|
|
2834
3078
|
const rootDir = process.cwd();
|
|
2835
|
-
const outputDir =
|
|
2836
|
-
if (
|
|
2837
|
-
|
|
3079
|
+
const outputDir = path7.join(rootDir, ".float");
|
|
3080
|
+
if (fs5.existsSync(outputDir)) {
|
|
3081
|
+
fs5.rmSync(outputDir, { recursive: true });
|
|
2838
3082
|
}
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
console.log(
|
|
3083
|
+
fs5.mkdirSync(outputDir, { recursive: true });
|
|
3084
|
+
fs5.mkdirSync(path7.join(outputDir, "pages"), { recursive: true });
|
|
3085
|
+
fs5.mkdirSync(path7.join(outputDir, "static"), { recursive: true });
|
|
3086
|
+
fs5.mkdirSync(path7.join(outputDir, "server"), { recursive: true });
|
|
3087
|
+
console.log(pc3.dim(" Scanning routes..."));
|
|
2844
3088
|
const routes = await scanRoutes(rootDir);
|
|
2845
3089
|
const pageRoutes = routes.filter((r) => r.type === "page" && !r.params.length);
|
|
2846
3090
|
const dynamicRoutes = routes.filter((r) => r.type === "page" && r.params.length > 0);
|
|
2847
3091
|
const apiRoutes = routes.filter((r) => r.type === "api");
|
|
2848
|
-
console.log(
|
|
2849
|
-
console.log(
|
|
3092
|
+
console.log(pc3.dim(` Found ${pageRoutes.length} static pages, ${dynamicRoutes.length} dynamic routes, ${apiRoutes.length} API routes`));
|
|
3093
|
+
console.log(pc3.dim(" Building client bundle..."));
|
|
2850
3094
|
const clientEntryPoints = routes.filter((r) => r.type === "page").map((r) => r.absolutePath);
|
|
2851
3095
|
if (clientEntryPoints.length > 0) {
|
|
2852
3096
|
await esbuild2.build({
|
|
2853
3097
|
entryPoints: clientEntryPoints,
|
|
2854
3098
|
bundle: true,
|
|
2855
|
-
outdir:
|
|
3099
|
+
outdir: path7.join(outputDir, "static", "_float"),
|
|
2856
3100
|
format: "esm",
|
|
2857
3101
|
splitting: true,
|
|
2858
3102
|
minify: opts.minify,
|
|
@@ -2874,13 +3118,13 @@ async function build2(options = {}) {
|
|
|
2874
3118
|
metafile: opts.analyze
|
|
2875
3119
|
});
|
|
2876
3120
|
}
|
|
2877
|
-
console.log(
|
|
3121
|
+
console.log(pc3.dim(" Building server bundle..."));
|
|
2878
3122
|
const serverEntryPoints = routes.map((r) => r.absolutePath);
|
|
2879
3123
|
if (serverEntryPoints.length > 0) {
|
|
2880
3124
|
await esbuild2.build({
|
|
2881
3125
|
entryPoints: serverEntryPoints,
|
|
2882
3126
|
bundle: true,
|
|
2883
|
-
outdir:
|
|
3127
|
+
outdir: path7.join(outputDir, "server"),
|
|
2884
3128
|
format: "esm",
|
|
2885
3129
|
platform: "node",
|
|
2886
3130
|
target: ["node18"],
|
|
@@ -2897,26 +3141,26 @@ async function build2(options = {}) {
|
|
|
2897
3141
|
external: ["react", "react-dom", "@float/core"]
|
|
2898
3142
|
});
|
|
2899
3143
|
}
|
|
2900
|
-
console.log(
|
|
3144
|
+
console.log(pc3.dim(" Pre-rendering static pages..."));
|
|
2901
3145
|
const prerenderedPages = [];
|
|
2902
3146
|
for (const route of pageRoutes) {
|
|
2903
3147
|
try {
|
|
2904
3148
|
const html = await renderPage(route, {}, { isDev: false });
|
|
2905
|
-
const outputPath = route.path === "/" ?
|
|
2906
|
-
|
|
2907
|
-
|
|
3149
|
+
const outputPath = route.path === "/" ? path7.join(outputDir, "pages", "index.html") : path7.join(outputDir, "pages", route.path, "index.html");
|
|
3150
|
+
fs5.mkdirSync(path7.dirname(outputPath), { recursive: true });
|
|
3151
|
+
fs5.writeFileSync(outputPath, html);
|
|
2908
3152
|
prerenderedPages.push(route.path);
|
|
2909
|
-
console.log(
|
|
3153
|
+
console.log(pc3.dim(` \u2713 ${route.path}`));
|
|
2910
3154
|
} catch (error) {
|
|
2911
|
-
console.log(
|
|
3155
|
+
console.log(pc3.yellow(` \u26A0 ${route.path} (will render at runtime)`));
|
|
2912
3156
|
}
|
|
2913
3157
|
}
|
|
2914
|
-
console.log(
|
|
3158
|
+
console.log(pc3.dim(" Building API routes..."));
|
|
2915
3159
|
for (const route of apiRoutes) {
|
|
2916
3160
|
await esbuild2.build({
|
|
2917
3161
|
entryPoints: [route.absolutePath],
|
|
2918
3162
|
bundle: true,
|
|
2919
|
-
outfile:
|
|
3163
|
+
outfile: path7.join(outputDir, "server", "api", `${route.path.replace(/\//g, "_")}.js`),
|
|
2920
3164
|
format: "esm",
|
|
2921
3165
|
platform: "neutral",
|
|
2922
3166
|
// Edge compatible
|
|
@@ -2924,10 +3168,10 @@ async function build2(options = {}) {
|
|
|
2924
3168
|
minify: true
|
|
2925
3169
|
});
|
|
2926
3170
|
}
|
|
2927
|
-
const publicDir =
|
|
2928
|
-
if (
|
|
2929
|
-
console.log(
|
|
2930
|
-
copyDir(publicDir,
|
|
3171
|
+
const publicDir = path7.join(rootDir, "public");
|
|
3172
|
+
if (fs5.existsSync(publicDir)) {
|
|
3173
|
+
console.log(pc3.dim(" Copying public assets..."));
|
|
3174
|
+
copyDir(publicDir, path7.join(outputDir, "static"));
|
|
2931
3175
|
}
|
|
2932
3176
|
const manifest = {
|
|
2933
3177
|
version: 1,
|
|
@@ -2936,19 +3180,19 @@ async function build2(options = {}) {
|
|
|
2936
3180
|
path: r.path,
|
|
2937
3181
|
type: r.type,
|
|
2938
3182
|
filePath: r.filePath,
|
|
2939
|
-
absolutePath:
|
|
3183
|
+
absolutePath: path7.relative(rootDir, r.absolutePath),
|
|
2940
3184
|
params: r.params,
|
|
2941
3185
|
isCatchAll: r.isCatchAll,
|
|
2942
3186
|
isOptionalCatchAll: r.isOptionalCatchAll,
|
|
2943
|
-
layouts: r.layouts.map((l) =>
|
|
3187
|
+
layouts: r.layouts.map((l) => path7.relative(rootDir, l)),
|
|
2944
3188
|
prerendered: prerenderedPages.includes(r.path)
|
|
2945
3189
|
})),
|
|
2946
3190
|
staticPages: prerenderedPages,
|
|
2947
3191
|
dynamicRoutes: dynamicRoutes.map((r) => r.path),
|
|
2948
3192
|
apiRoutes: apiRoutes.map((r) => r.path)
|
|
2949
3193
|
};
|
|
2950
|
-
|
|
2951
|
-
|
|
3194
|
+
fs5.writeFileSync(
|
|
3195
|
+
path7.join(outputDir, "routes-manifest.json"),
|
|
2952
3196
|
JSON.stringify(manifest, null, 2)
|
|
2953
3197
|
);
|
|
2954
3198
|
const duration = Date.now() - startTime;
|
|
@@ -2958,16 +3202,16 @@ async function build2(options = {}) {
|
|
|
2958
3202
|
routes: routes.length,
|
|
2959
3203
|
pages: prerenderedPages.length
|
|
2960
3204
|
};
|
|
2961
|
-
|
|
2962
|
-
|
|
3205
|
+
fs5.writeFileSync(
|
|
3206
|
+
path7.join(outputDir, "build-info.json"),
|
|
2963
3207
|
JSON.stringify(buildInfo, null, 2)
|
|
2964
3208
|
);
|
|
2965
3209
|
console.log("");
|
|
2966
|
-
console.log(
|
|
2967
|
-
console.log(
|
|
2968
|
-
console.log(
|
|
2969
|
-
console.log(
|
|
2970
|
-
console.log(
|
|
3210
|
+
console.log(pc3.bold(" Build Summary:"));
|
|
3211
|
+
console.log(pc3.dim(` Static Pages: ${prerenderedPages.length}`));
|
|
3212
|
+
console.log(pc3.dim(` Dynamic Routes: ${dynamicRoutes.length}`));
|
|
3213
|
+
console.log(pc3.dim(` API Routes: ${apiRoutes.length}`));
|
|
3214
|
+
console.log(pc3.dim(` Output: .float/`));
|
|
2971
3215
|
return {
|
|
2972
3216
|
routes,
|
|
2973
3217
|
duration,
|
|
@@ -2977,15 +3221,15 @@ async function build2(options = {}) {
|
|
|
2977
3221
|
};
|
|
2978
3222
|
}
|
|
2979
3223
|
function copyDir(src, dest) {
|
|
2980
|
-
|
|
2981
|
-
const entries =
|
|
3224
|
+
fs5.mkdirSync(dest, { recursive: true });
|
|
3225
|
+
const entries = fs5.readdirSync(src, { withFileTypes: true });
|
|
2982
3226
|
for (const entry of entries) {
|
|
2983
|
-
const srcPath =
|
|
2984
|
-
const destPath =
|
|
3227
|
+
const srcPath = path7.join(src, entry.name);
|
|
3228
|
+
const destPath = path7.join(dest, entry.name);
|
|
2985
3229
|
if (entry.isDirectory()) {
|
|
2986
3230
|
copyDir(srcPath, destPath);
|
|
2987
3231
|
} else {
|
|
2988
|
-
|
|
3232
|
+
fs5.copyFileSync(srcPath, destPath);
|
|
2989
3233
|
}
|
|
2990
3234
|
}
|
|
2991
3235
|
}
|
|
@@ -2993,45 +3237,45 @@ function copyDir(src, dest) {
|
|
|
2993
3237
|
// src/server/prod-server.ts
|
|
2994
3238
|
init_esm_shims();
|
|
2995
3239
|
import http2 from "http";
|
|
2996
|
-
import
|
|
2997
|
-
import
|
|
2998
|
-
import
|
|
3240
|
+
import fs6 from "fs";
|
|
3241
|
+
import path8 from "path";
|
|
3242
|
+
import pc4 from "picocolors";
|
|
2999
3243
|
import mime2 from "mime-types";
|
|
3000
3244
|
var cachedRoutes = [];
|
|
3001
3245
|
var pageCache = /* @__PURE__ */ new Map();
|
|
3002
3246
|
async function startProductionServer(options) {
|
|
3003
3247
|
const { port, host } = options;
|
|
3004
3248
|
const rootDir = process.cwd();
|
|
3005
|
-
const distDir =
|
|
3006
|
-
const publicDir =
|
|
3007
|
-
const manifestPath =
|
|
3008
|
-
if (
|
|
3009
|
-
const manifest = JSON.parse(
|
|
3249
|
+
const distDir = path8.join(rootDir, ".float");
|
|
3250
|
+
const publicDir = path8.join(rootDir, "public");
|
|
3251
|
+
const manifestPath = path8.join(distDir, "routes-manifest.json");
|
|
3252
|
+
if (fs6.existsSync(manifestPath)) {
|
|
3253
|
+
const manifest = JSON.parse(fs6.readFileSync(manifestPath, "utf-8"));
|
|
3010
3254
|
cachedRoutes = manifest.routes;
|
|
3011
|
-
console.log(
|
|
3255
|
+
console.log(pc4.dim(` \u{1F4E6} Loaded ${cachedRoutes.length} routes from manifest`));
|
|
3012
3256
|
} else {
|
|
3013
|
-
console.error(
|
|
3257
|
+
console.error(pc4.red(" \u274C No build manifest found. Run `float build` first."));
|
|
3014
3258
|
process.exit(1);
|
|
3015
3259
|
}
|
|
3016
|
-
const pagesDir =
|
|
3017
|
-
if (
|
|
3018
|
-
const prerenderedFiles =
|
|
3260
|
+
const pagesDir = path8.join(distDir, "pages");
|
|
3261
|
+
if (fs6.existsSync(pagesDir)) {
|
|
3262
|
+
const prerenderedFiles = fs6.readdirSync(pagesDir, { recursive: true });
|
|
3019
3263
|
for (const file of prerenderedFiles) {
|
|
3020
3264
|
if (file.endsWith(".html")) {
|
|
3021
3265
|
const routePath = "/" + file.replace(/\.html$/, "").replace(/index$/, "");
|
|
3022
|
-
const content =
|
|
3266
|
+
const content = fs6.readFileSync(path8.join(pagesDir, file), "utf-8");
|
|
3023
3267
|
pageCache.set(routePath, content);
|
|
3024
3268
|
}
|
|
3025
3269
|
}
|
|
3026
|
-
console.log(
|
|
3270
|
+
console.log(pc4.dim(` \u{1F4C4} Loaded ${pageCache.size} pre-rendered pages`));
|
|
3027
3271
|
}
|
|
3028
3272
|
const server = http2.createServer(async (req, res) => {
|
|
3029
3273
|
const url = new URL(req.url || "/", `http://${host}:${port}`);
|
|
3030
3274
|
const pathname = url.pathname;
|
|
3031
3275
|
try {
|
|
3032
|
-
const staticPath =
|
|
3033
|
-
if (
|
|
3034
|
-
const content =
|
|
3276
|
+
const staticPath = path8.join(distDir, "static", pathname);
|
|
3277
|
+
if (fs6.existsSync(staticPath) && fs6.statSync(staticPath).isFile()) {
|
|
3278
|
+
const content = fs6.readFileSync(staticPath);
|
|
3035
3279
|
const contentType = mime2.lookup(staticPath) || "application/octet-stream";
|
|
3036
3280
|
res.writeHead(200, {
|
|
3037
3281
|
"Content-Type": contentType,
|
|
@@ -3040,9 +3284,9 @@ async function startProductionServer(options) {
|
|
|
3040
3284
|
res.end(content);
|
|
3041
3285
|
return;
|
|
3042
3286
|
}
|
|
3043
|
-
const publicFilePath =
|
|
3044
|
-
if (
|
|
3045
|
-
const content =
|
|
3287
|
+
const publicFilePath = path8.join(publicDir, pathname);
|
|
3288
|
+
if (fs6.existsSync(publicFilePath) && fs6.statSync(publicFilePath).isFile()) {
|
|
3289
|
+
const content = fs6.readFileSync(publicFilePath);
|
|
3046
3290
|
const contentType = mime2.lookup(publicFilePath) || "application/octet-stream";
|
|
3047
3291
|
res.writeHead(200, { "Content-Type": contentType });
|
|
3048
3292
|
res.end(content);
|
|
@@ -3075,13 +3319,13 @@ async function startProductionServer(options) {
|
|
|
3075
3319
|
});
|
|
3076
3320
|
res.end(html);
|
|
3077
3321
|
} catch (error) {
|
|
3078
|
-
console.error(
|
|
3322
|
+
console.error(pc4.red("Request error:"), error);
|
|
3079
3323
|
res.writeHead(500, { "Content-Type": "text/html" });
|
|
3080
3324
|
res.end("<h1>500 - Internal Server Error</h1>");
|
|
3081
3325
|
}
|
|
3082
3326
|
});
|
|
3083
3327
|
server.listen(port, host, () => {
|
|
3084
|
-
console.log(
|
|
3328
|
+
console.log(pc4.green(` \u2705 Production server running at ${pc4.cyan(`http://${host}:${port}`)}
|
|
3085
3329
|
`));
|
|
3086
3330
|
});
|
|
3087
3331
|
}
|
|
@@ -3093,14 +3337,14 @@ var VERSION = "2.0.4";
|
|
|
3093
3337
|
// src/cli/index.ts
|
|
3094
3338
|
var cli = cac("float");
|
|
3095
3339
|
var banner = `
|
|
3096
|
-
${
|
|
3097
|
-
${
|
|
3098
|
-
${
|
|
3099
|
-
${
|
|
3340
|
+
${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")}
|
|
3341
|
+
${pc5.cyan("\u2551")} ${pc5.bold(pc5.magenta("\u26A1 Float.js"))} ${pc5.dim(`v${VERSION}`)} ${pc5.cyan("\u2551")}
|
|
3342
|
+
${pc5.cyan("\u2551")} ${pc5.dim("Ultra Modern Web Framework")} ${pc5.cyan("\u2551")}
|
|
3343
|
+
${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
3344
|
`;
|
|
3101
3345
|
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
3346
|
console.log(banner);
|
|
3103
|
-
console.log(
|
|
3347
|
+
console.log(pc5.cyan("\u{1F680} Starting development server...\n"));
|
|
3104
3348
|
try {
|
|
3105
3349
|
const server = await createDevServer({
|
|
3106
3350
|
port: Number(options.port),
|
|
@@ -3109,45 +3353,45 @@ cli.command("dev", "Start development server").option("-p, --port <port>", "Port
|
|
|
3109
3353
|
});
|
|
3110
3354
|
await server.start();
|
|
3111
3355
|
} catch (error) {
|
|
3112
|
-
console.error(
|
|
3356
|
+
console.error(pc5.red("\u274C Failed to start dev server:"), error);
|
|
3113
3357
|
process.exit(1);
|
|
3114
3358
|
}
|
|
3115
3359
|
});
|
|
3116
3360
|
cli.command("build", "Build for production").option("--analyze", "Analyze bundle size", { default: false }).action(async (options) => {
|
|
3117
3361
|
console.log(banner);
|
|
3118
|
-
console.log(
|
|
3362
|
+
console.log(pc5.cyan("\u{1F4E6} Building for production...\n"));
|
|
3119
3363
|
try {
|
|
3120
3364
|
const startTime = Date.now();
|
|
3121
3365
|
await build2({ analyze: options.analyze });
|
|
3122
3366
|
const duration = Date.now() - startTime;
|
|
3123
|
-
console.log(
|
|
3367
|
+
console.log(pc5.green(`
|
|
3124
3368
|
\u2705 Build completed in ${duration}ms`));
|
|
3125
3369
|
} catch (error) {
|
|
3126
|
-
console.error(
|
|
3370
|
+
console.error(pc5.red("\u274C Build failed:"), error);
|
|
3127
3371
|
process.exit(1);
|
|
3128
3372
|
}
|
|
3129
3373
|
});
|
|
3130
3374
|
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
3375
|
console.log(banner);
|
|
3132
|
-
console.log(
|
|
3376
|
+
console.log(pc5.cyan("\u{1F310} Starting production server...\n"));
|
|
3133
3377
|
try {
|
|
3134
3378
|
await startProductionServer({
|
|
3135
3379
|
port: Number(options.port),
|
|
3136
3380
|
host: options.host
|
|
3137
3381
|
});
|
|
3138
3382
|
} catch (error) {
|
|
3139
|
-
console.error(
|
|
3383
|
+
console.error(pc5.red("\u274C Failed to start server:"), error);
|
|
3140
3384
|
process.exit(1);
|
|
3141
3385
|
}
|
|
3142
3386
|
});
|
|
3143
3387
|
cli.command("info", "Show environment information").action(() => {
|
|
3144
3388
|
console.log(banner);
|
|
3145
|
-
console.log(
|
|
3146
|
-
console.log(` ${
|
|
3147
|
-
console.log(` ${
|
|
3148
|
-
console.log(` ${
|
|
3149
|
-
console.log(` ${
|
|
3150
|
-
console.log(` ${
|
|
3389
|
+
console.log(pc5.bold("Environment Info:\n"));
|
|
3390
|
+
console.log(` ${pc5.dim("Float.js:")} v${VERSION}`);
|
|
3391
|
+
console.log(` ${pc5.dim("Node.js:")} ${process.version}`);
|
|
3392
|
+
console.log(` ${pc5.dim("Platform:")} ${process.platform}`);
|
|
3393
|
+
console.log(` ${pc5.dim("Arch:")} ${process.arch}`);
|
|
3394
|
+
console.log(` ${pc5.dim("CWD:")} ${process.cwd()}`);
|
|
3151
3395
|
});
|
|
3152
3396
|
cli.help();
|
|
3153
3397
|
cli.version(VERSION);
|