raktajs 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/build.d.ts.map +1 -1
- package/dist/cli/dev.d.ts.map +1 -1
- package/dist/cli/rakta.js +343 -89
- package/dist/cli/rakta.js.map +9 -8
- package/dist/forge/build.d.ts.map +1 -1
- package/dist/forge/clientEntry.d.ts +9 -0
- package/dist/forge/clientEntry.d.ts.map +1 -0
- package/dist/forge/devServer.d.ts +1 -1
- package/dist/forge/devServer.d.ts.map +1 -1
- package/dist/forge/index.js +267 -19
- package/dist/forge/index.js.map +6 -5
- package/dist/forge/types.d.ts +2 -0
- package/dist/forge/types.d.ts.map +1 -1
- package/dist/index.js +273 -25
- package/dist/index.js.map +7 -6
- package/dist/tide/index.js +2 -2
- package/dist/tide/index.js.map +2 -2
- package/package.json +1 -1
package/dist/cli/rakta.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// @bun
|
|
3
3
|
|
|
4
4
|
// src/cli/rakta.ts
|
|
5
|
-
import { join as
|
|
5
|
+
import { join as join18 } from "path";
|
|
6
6
|
|
|
7
7
|
// src/config/loadConfig.ts
|
|
8
8
|
import { existsSync } from "fs";
|
|
@@ -474,18 +474,225 @@ function printInspectReport(report) {
|
|
|
474
474
|
}
|
|
475
475
|
|
|
476
476
|
// src/cli/build.ts
|
|
477
|
-
import { isAbsolute, join as
|
|
477
|
+
import { isAbsolute, join as join6 } from "path";
|
|
478
|
+
|
|
479
|
+
// src/forge/build.ts
|
|
480
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2 } from "fs";
|
|
481
|
+
import { join as join5, resolve as resolve2 } from "path";
|
|
482
|
+
|
|
483
|
+
// src/forge/clientEntry.ts
|
|
484
|
+
import { existsSync as existsSync5, mkdirSync, writeFileSync as writeFileSync2 } from "fs";
|
|
485
|
+
import { dirname, join as join4, relative as relative2 } from "path";
|
|
486
|
+
function toModuleSpecifier(fromFile, targetFile) {
|
|
487
|
+
const relativePath = relative2(dirname(fromFile), targetFile).replace(/\\/g, "/");
|
|
488
|
+
if (relativePath.startsWith(".")) {
|
|
489
|
+
return relativePath;
|
|
490
|
+
}
|
|
491
|
+
return `./${relativePath}`;
|
|
492
|
+
}
|
|
493
|
+
function toCssImportSpecifier(entryPath, projectRoot) {
|
|
494
|
+
const candidates = [
|
|
495
|
+
join4(projectRoot, "styles", "globals.css"),
|
|
496
|
+
join4(projectRoot, "styles", "globals.scss"),
|
|
497
|
+
join4(projectRoot, "styles", "globals.sass")
|
|
498
|
+
];
|
|
499
|
+
const stylePath = candidates.find((candidate) => existsSync5(candidate));
|
|
500
|
+
if (!stylePath) {
|
|
501
|
+
return null;
|
|
502
|
+
}
|
|
503
|
+
return toModuleSpecifier(entryPath, stylePath);
|
|
504
|
+
}
|
|
505
|
+
function getPageRoutes(manifest) {
|
|
506
|
+
return manifest.routes.filter((route) => route.kind === "page");
|
|
507
|
+
}
|
|
508
|
+
function buildRouteImports(entryPath, appDir, pageRoutes) {
|
|
509
|
+
const routeEntries = pageRoutes.map((route) => {
|
|
510
|
+
const pagePath = join4(appDir, route.filePath);
|
|
511
|
+
return ` ${JSON.stringify(route.urlPattern)}: () => import("${toModuleSpecifier(entryPath, pagePath)}"),`;
|
|
512
|
+
}).join(`
|
|
513
|
+
`);
|
|
514
|
+
return `const routeModules = {
|
|
515
|
+
${routeEntries}
|
|
516
|
+
} as const;`;
|
|
517
|
+
}
|
|
518
|
+
function buildRouteTable(pageRoutes) {
|
|
519
|
+
const routeEntries = pageRoutes.map((route) => ` ${JSON.stringify(route.urlPattern)}: routeModules[${JSON.stringify(route.urlPattern)}],`).join(`
|
|
520
|
+
`);
|
|
521
|
+
return `const routes = {
|
|
522
|
+
${routeEntries}
|
|
523
|
+
} as const;`;
|
|
524
|
+
}
|
|
525
|
+
function findExistingModule(basePathWithoutExtension) {
|
|
526
|
+
const candidates = [".tsx", ".ts", ".jsx", ".js"].map((extension) => `${basePathWithoutExtension}${extension}`);
|
|
527
|
+
return candidates.find((candidate) => existsSync5(candidate));
|
|
528
|
+
}
|
|
529
|
+
function buildStarterGlobalLoaders(entryPath, appDir) {
|
|
530
|
+
const mascotPath = findExistingModule(join4(appDir, "components", "raktaShrimpMascot"));
|
|
531
|
+
const gamePath = findExistingModule(join4(appDir, "components", "shrimpRunGame"));
|
|
532
|
+
const loaders = [];
|
|
533
|
+
if (mascotPath !== undefined) {
|
|
534
|
+
loaders.push(` const mascotModule = await import("${toModuleSpecifier(entryPath, mascotPath)}");
|
|
535
|
+
(globalThis as typeof globalThis & Record<string, unknown>).RaktaShrimpMascot = mascotModule.default;`);
|
|
536
|
+
}
|
|
537
|
+
if (gamePath !== undefined) {
|
|
538
|
+
loaders.push(` const gameModule = await import("${toModuleSpecifier(entryPath, gamePath)}");
|
|
539
|
+
(globalThis as typeof globalThis & Record<string, unknown>).ShrimpRunGame = gameModule.default;`);
|
|
540
|
+
}
|
|
541
|
+
if (loaders.length === 0) {
|
|
542
|
+
return `async function loadRaktaGlobals(): Promise<void> {
|
|
543
|
+
return;
|
|
544
|
+
}`;
|
|
545
|
+
}
|
|
546
|
+
return `async function loadRaktaGlobals(): Promise<void> {
|
|
547
|
+
${loaders.join(`
|
|
548
|
+
|
|
549
|
+
`)}
|
|
550
|
+
}`;
|
|
551
|
+
}
|
|
552
|
+
function buildClientEntrySource(options, entryPath) {
|
|
553
|
+
const pageRoutes = getPageRoutes(options.manifest);
|
|
554
|
+
const routeModules = buildRouteImports(entryPath, options.appDir, pageRoutes);
|
|
555
|
+
const routeTable = buildRouteTable(pageRoutes);
|
|
556
|
+
const cssImportSpecifier = toCssImportSpecifier(entryPath, options.projectRoot);
|
|
557
|
+
const cssImport = cssImportSpecifier !== null ? `import "${cssImportSpecifier}";
|
|
558
|
+
` : "";
|
|
559
|
+
const starterGlobalLoaders = buildStarterGlobalLoaders(entryPath, options.appDir);
|
|
560
|
+
return `import React, { useEffect, useState } from "react";
|
|
561
|
+
import { createRoot } from "react-dom/client";
|
|
562
|
+
import * as ReactHooks from "react";
|
|
563
|
+
${cssImport}
|
|
564
|
+
(globalThis as typeof globalThis & Record<string, unknown>).useCallback = ReactHooks.useCallback;
|
|
565
|
+
(globalThis as typeof globalThis & Record<string, unknown>).useEffect = ReactHooks.useEffect;
|
|
566
|
+
(globalThis as typeof globalThis & Record<string, unknown>).useRef = ReactHooks.useRef;
|
|
567
|
+
(globalThis as typeof globalThis & Record<string, unknown>).useState = ReactHooks.useState;
|
|
568
|
+
|
|
569
|
+
${starterGlobalLoaders}
|
|
570
|
+
|
|
571
|
+
await loadRaktaGlobals();
|
|
572
|
+
|
|
573
|
+
${routeModules}
|
|
574
|
+
|
|
575
|
+
${routeTable}
|
|
576
|
+
|
|
577
|
+
type RoutePath = keyof typeof routes;
|
|
578
|
+
type PageModule = { default: React.ComponentType };
|
|
579
|
+
|
|
580
|
+
function normalizePathname(pathname: string): string {
|
|
581
|
+
if (pathname.length > 1 && pathname.endsWith("/")) {
|
|
582
|
+
return pathname.slice(0, -1);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
return pathname;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function resolveRouteLoader(pathname: string): () => Promise<PageModule> {
|
|
589
|
+
const normalizedPathname = normalizePathname(pathname) as RoutePath;
|
|
590
|
+
|
|
591
|
+
return routes[normalizedPathname] ?? routes["/"];
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
function navigate(to: string): void {
|
|
595
|
+
window.history.pushState({ source: "rakta-click", to }, "", to);
|
|
596
|
+
window.dispatchEvent(new PopStateEvent("popstate", { state: { to } }));
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function App(): React.ReactElement {
|
|
600
|
+
const [pathname, setPathname] = useState(() => window.location.pathname);
|
|
601
|
+
const [Page, setPage] = useState<React.ComponentType | null>(null);
|
|
602
|
+
|
|
603
|
+
useEffect(() => {
|
|
604
|
+
function handlePopState(): void {
|
|
605
|
+
setPathname(window.location.pathname);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
function handleClick(event: MouseEvent): void {
|
|
609
|
+
const target = event.target;
|
|
610
|
+
|
|
611
|
+
if (!(target instanceof Element)) {
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
const clickElement = target.closest("click");
|
|
616
|
+
|
|
617
|
+
if (!clickElement) {
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
const to = clickElement.getAttribute("to");
|
|
622
|
+
|
|
623
|
+
if (!to || to.startsWith("http://") || to.startsWith("https://")) {
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
event.preventDefault();
|
|
628
|
+
navigate(to);
|
|
629
|
+
setPathname(window.location.pathname);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
window.addEventListener("popstate", handlePopState);
|
|
633
|
+
document.addEventListener("click", handleClick);
|
|
634
|
+
|
|
635
|
+
return () => {
|
|
636
|
+
window.removeEventListener("popstate", handlePopState);
|
|
637
|
+
document.removeEventListener("click", handleClick);
|
|
638
|
+
};
|
|
639
|
+
}, []);
|
|
640
|
+
|
|
641
|
+
useEffect(() => {
|
|
642
|
+
let isCurrent = true;
|
|
643
|
+
|
|
644
|
+
resolveRouteLoader(pathname)().then((pageModule) => {
|
|
645
|
+
if (isCurrent) {
|
|
646
|
+
setPage(() => pageModule.default);
|
|
647
|
+
}
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
return () => {
|
|
651
|
+
isCurrent = false;
|
|
652
|
+
};
|
|
653
|
+
}, [pathname]);
|
|
654
|
+
|
|
655
|
+
if (!Page) {
|
|
656
|
+
return React.createElement("main", {
|
|
657
|
+
style: {
|
|
658
|
+
minHeight: "100vh",
|
|
659
|
+
display: "grid",
|
|
660
|
+
placeItems: "center",
|
|
661
|
+
background: "#050505",
|
|
662
|
+
color: "#f8fafc",
|
|
663
|
+
fontFamily: "ui-sans-serif, system-ui, sans-serif",
|
|
664
|
+
},
|
|
665
|
+
}, "Loading Rakta.js...");
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
return React.createElement(Page);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
const rootElement = document.getElementById("rakta-root");
|
|
672
|
+
|
|
673
|
+
if (!rootElement) {
|
|
674
|
+
throw new Error("Rakta.js root element #rakta-root was not found.");
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
createRoot(rootElement).render(React.createElement(App));
|
|
678
|
+
`;
|
|
679
|
+
}
|
|
680
|
+
function writeClientEntry(options) {
|
|
681
|
+
mkdirSync(options.workDir, { recursive: true });
|
|
682
|
+
const entryPath = join4(options.workDir, "client-entry.tsx");
|
|
683
|
+
const entrySource = buildClientEntrySource(options, entryPath);
|
|
684
|
+
writeFileSync2(entryPath, entrySource, "utf-8");
|
|
685
|
+
return entryPath;
|
|
686
|
+
}
|
|
478
687
|
|
|
479
688
|
// src/forge/build.ts
|
|
480
|
-
import { mkdirSync } from "fs";
|
|
481
|
-
import { join as join4, resolve as resolve2 } from "path";
|
|
482
689
|
async function buildProject(options) {
|
|
483
690
|
const startMs = Date.now();
|
|
484
691
|
const artifacts = [];
|
|
485
692
|
const errors = [];
|
|
486
|
-
|
|
693
|
+
mkdirSync2(resolve2(options.outDir), { recursive: true });
|
|
487
694
|
const manifest = generateManifest(options.appDir);
|
|
488
|
-
const manifestPath =
|
|
695
|
+
const manifestPath = join5(options.outDir, "route-manifest.json");
|
|
489
696
|
writeManifest(manifest, manifestPath);
|
|
490
697
|
const manifestContent = JSON.stringify(manifest);
|
|
491
698
|
artifacts.push({
|
|
@@ -493,15 +700,21 @@ async function buildProject(options) {
|
|
|
493
700
|
sizeBytes: new TextEncoder().encode(manifestContent).byteLength,
|
|
494
701
|
kind: "manifest"
|
|
495
702
|
});
|
|
703
|
+
const entryPoint = existsSync6(options.entryPoint) ? options.entryPoint : writeClientEntry({
|
|
704
|
+
projectRoot: options.projectRoot,
|
|
705
|
+
appDir: options.appDir,
|
|
706
|
+
workDir: join5(options.projectRoot, ".rakta"),
|
|
707
|
+
manifest
|
|
708
|
+
});
|
|
496
709
|
const buildResult = await Bun.build({
|
|
497
|
-
entrypoints: [
|
|
710
|
+
entrypoints: [entryPoint],
|
|
498
711
|
outdir: options.outDir,
|
|
499
712
|
target: options.target,
|
|
500
713
|
minify: options.minify,
|
|
501
714
|
sourcemap: options.sourcemap ? "external" : "none",
|
|
502
715
|
splitting: options.splitting,
|
|
503
716
|
naming: {
|
|
504
|
-
entry: "
|
|
717
|
+
entry: "app.[ext]",
|
|
505
718
|
chunk: "chunks/[name]-[hash].[ext]",
|
|
506
719
|
asset: "assets/[name]-[hash].[ext]"
|
|
507
720
|
}
|
|
@@ -539,7 +752,7 @@ function resolveProjectPath(cwd, pathValue) {
|
|
|
539
752
|
if (isAbsolute(pathValue)) {
|
|
540
753
|
return pathValue;
|
|
541
754
|
}
|
|
542
|
-
return
|
|
755
|
+
return join6(cwd, pathValue);
|
|
543
756
|
}
|
|
544
757
|
async function buildCommand(cwd = process.cwd()) {
|
|
545
758
|
const projectConfig = await loadConfig(cwd);
|
|
@@ -547,16 +760,17 @@ async function buildCommand(cwd = process.cwd()) {
|
|
|
547
760
|
const publicDirectory = projectConfig.publicDir ?? "public";
|
|
548
761
|
const outputDirectory = projectConfig.build.outDir ?? "dist";
|
|
549
762
|
const entryFile = projectConfig.build.entryPoint ?? "entry.client.tsx";
|
|
550
|
-
const entryPoint = resolveProjectPath(
|
|
763
|
+
const entryPoint = resolveProjectPath(join6(cwd, appDirectory), entryFile);
|
|
551
764
|
const outDir = resolveProjectPath(cwd, outputDirectory);
|
|
552
765
|
console.log(`
|
|
553
766
|
Building Rakta.js application...
|
|
554
767
|
`);
|
|
555
768
|
const buildResult = await buildProject({
|
|
769
|
+
projectRoot: cwd,
|
|
556
770
|
entryPoint,
|
|
557
771
|
outDir,
|
|
558
|
-
appDir:
|
|
559
|
-
publicDir:
|
|
772
|
+
appDir: join6(cwd, appDirectory),
|
|
773
|
+
publicDir: join6(cwd, publicDirectory),
|
|
560
774
|
appName: projectConfig.appName,
|
|
561
775
|
sourcemap: projectConfig.build.sourcemap ?? false,
|
|
562
776
|
minify: projectConfig.build.minify ?? true,
|
|
@@ -578,11 +792,11 @@ async function buildCommand(cwd = process.cwd()) {
|
|
|
578
792
|
}
|
|
579
793
|
|
|
580
794
|
// src/cli/dev.ts
|
|
581
|
-
import { join as
|
|
795
|
+
import { join as join8 } from "path";
|
|
582
796
|
|
|
583
797
|
// src/forge/devServer.ts
|
|
584
|
-
import { existsSync as
|
|
585
|
-
import { join as
|
|
798
|
+
import { existsSync as existsSync7, readFileSync as readFileSync2, statSync as statSync3 } from "fs";
|
|
799
|
+
import { join as join7 } from "path";
|
|
586
800
|
|
|
587
801
|
// src/render/renderer.ts
|
|
588
802
|
function buildHtmlShell(options) {
|
|
@@ -700,18 +914,55 @@ function resolveDevPort(port) {
|
|
|
700
914
|
return port > 0 ? port : DEFAULT_DEV_PORT;
|
|
701
915
|
}
|
|
702
916
|
function isReadableFile(filePath) {
|
|
703
|
-
return
|
|
917
|
+
return existsSync7(filePath) && statSync3(filePath).isFile();
|
|
918
|
+
}
|
|
919
|
+
async function buildDevClientBundle(options, manifest) {
|
|
920
|
+
const workDir = join7(options.projectRoot, ".rakta");
|
|
921
|
+
const clientOutDir = join7(workDir, "dev");
|
|
922
|
+
const clientEntry = writeClientEntry({
|
|
923
|
+
projectRoot: options.projectRoot,
|
|
924
|
+
appDir: options.appDir,
|
|
925
|
+
workDir,
|
|
926
|
+
manifest
|
|
927
|
+
});
|
|
928
|
+
const buildResult = await Bun.build({
|
|
929
|
+
entrypoints: [clientEntry],
|
|
930
|
+
outdir: clientOutDir,
|
|
931
|
+
target: "browser",
|
|
932
|
+
sourcemap: "external",
|
|
933
|
+
naming: {
|
|
934
|
+
entry: "app.[ext]",
|
|
935
|
+
chunk: "chunks/[name]-[hash].[ext]",
|
|
936
|
+
asset: "assets/[name]-[hash].[ext]"
|
|
937
|
+
}
|
|
938
|
+
});
|
|
939
|
+
if (!buildResult.success) {
|
|
940
|
+
const buildErrors = buildResult.logs.map((buildLog) => buildLog.message).join(`
|
|
941
|
+
`);
|
|
942
|
+
throw new Error(`Failed to build Rakta.js client bundle.
|
|
943
|
+
${buildErrors}`);
|
|
944
|
+
}
|
|
945
|
+
return clientOutDir;
|
|
704
946
|
}
|
|
705
|
-
function startDevServer(options) {
|
|
947
|
+
async function startDevServer(options) {
|
|
706
948
|
const manifest = generateManifest(options.appDir);
|
|
707
949
|
const resolvedPort = resolveDevPort(options.port);
|
|
950
|
+
const clientOutDir = await buildDevClientBundle(options, manifest);
|
|
708
951
|
const server = Bun.serve({
|
|
709
952
|
port: resolvedPort,
|
|
710
953
|
hostname: options.host,
|
|
711
954
|
async fetch(request) {
|
|
712
955
|
const url = new URL(request.url);
|
|
713
956
|
const { pathname } = url;
|
|
714
|
-
|
|
957
|
+
if (clientOutDir.length > 0) {
|
|
958
|
+
const clientBundlePath = join7(clientOutDir, pathname);
|
|
959
|
+
if (isReadableFile(clientBundlePath)) {
|
|
960
|
+
return new Response(readFileSync2(clientBundlePath), {
|
|
961
|
+
headers: { "Content-Type": resolveMime(clientBundlePath) }
|
|
962
|
+
});
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
const publicPath = join7(options.publicDir, pathname);
|
|
715
966
|
if (isReadableFile(publicPath)) {
|
|
716
967
|
return new Response(readFileSync2(publicPath), {
|
|
717
968
|
headers: { "Content-Type": resolveMime(pathname) }
|
|
@@ -719,7 +970,7 @@ function startDevServer(options) {
|
|
|
719
970
|
}
|
|
720
971
|
const apiMatch = matchRoute(pathname, manifest.routes.filter((route) => route.kind === "api"));
|
|
721
972
|
if (apiMatch) {
|
|
722
|
-
const modulePath =
|
|
973
|
+
const modulePath = join7(options.appDir, apiMatch.entry.filePath);
|
|
723
974
|
const routeModule = await import(modulePath);
|
|
724
975
|
const method = request.method.toUpperCase();
|
|
725
976
|
const handler = routeModule[method];
|
|
@@ -747,7 +998,7 @@ function startDevServer(options) {
|
|
|
747
998
|
}, {
|
|
748
999
|
appName: options.appName,
|
|
749
1000
|
scriptPath: "/app.js",
|
|
750
|
-
cssPath: "/
|
|
1001
|
+
cssPath: "/app.css",
|
|
751
1002
|
lang: "en"
|
|
752
1003
|
});
|
|
753
1004
|
if (result.kind === "failure") {
|
|
@@ -774,19 +1025,22 @@ async function devCommand(cwd = process.cwd()) {
|
|
|
774
1025
|
console.log(`
|
|
775
1026
|
Starting Rakta.js dev server...
|
|
776
1027
|
`);
|
|
777
|
-
startDevServer({
|
|
1028
|
+
const server = await startDevServer({
|
|
1029
|
+
projectRoot: cwd,
|
|
778
1030
|
port: config.port,
|
|
779
1031
|
host: config.server.hostname ?? "0.0.0.0",
|
|
780
|
-
appDir:
|
|
781
|
-
publicDir:
|
|
1032
|
+
appDir: join8(cwd, config.appDir),
|
|
1033
|
+
publicDir: join8(cwd, config.publicDir),
|
|
782
1034
|
appName: config.appName,
|
|
783
1035
|
renderConfig: config.render
|
|
784
1036
|
});
|
|
1037
|
+
console.log(` Ready at ${server.url}
|
|
1038
|
+
`);
|
|
785
1039
|
}
|
|
786
1040
|
|
|
787
1041
|
// src/cli/doctor.ts
|
|
788
|
-
import { existsSync as
|
|
789
|
-
import { join as
|
|
1042
|
+
import { existsSync as existsSync8 } from "fs";
|
|
1043
|
+
import { join as join9 } from "path";
|
|
790
1044
|
async function doctorCommand(cwd = process.cwd()) {
|
|
791
1045
|
const config = await loadConfig(cwd);
|
|
792
1046
|
const checks = [
|
|
@@ -797,31 +1051,31 @@ async function doctorCommand(cwd = process.cwd()) {
|
|
|
797
1051
|
},
|
|
798
1052
|
{
|
|
799
1053
|
label: "rakta.config.ts",
|
|
800
|
-
passed:
|
|
1054
|
+
passed: existsSync8(join9(cwd, "rakta.config.ts")) || existsSync8(join9(cwd, "rakta.config.js"))
|
|
801
1055
|
},
|
|
802
1056
|
{
|
|
803
1057
|
label: `app/ directory (${config.appDir})`,
|
|
804
|
-
passed:
|
|
1058
|
+
passed: existsSync8(join9(cwd, config.appDir))
|
|
805
1059
|
},
|
|
806
1060
|
{
|
|
807
1061
|
label: "app/page.tsx (root page)",
|
|
808
|
-
passed:
|
|
1062
|
+
passed: existsSync8(join9(cwd, config.appDir, "page.tsx"))
|
|
809
1063
|
},
|
|
810
1064
|
{
|
|
811
1065
|
label: "app/layout.tsx (root layout)",
|
|
812
|
-
passed:
|
|
1066
|
+
passed: existsSync8(join9(cwd, config.appDir, "layout.tsx"))
|
|
813
1067
|
},
|
|
814
1068
|
{
|
|
815
1069
|
label: `public/ directory (${config.publicDir})`,
|
|
816
|
-
passed:
|
|
1070
|
+
passed: existsSync8(join9(cwd, config.publicDir))
|
|
817
1071
|
},
|
|
818
1072
|
{
|
|
819
1073
|
label: "package.json",
|
|
820
|
-
passed:
|
|
1074
|
+
passed: existsSync8(join9(cwd, "package.json"))
|
|
821
1075
|
},
|
|
822
1076
|
{
|
|
823
1077
|
label: "tsconfig.json",
|
|
824
|
-
passed:
|
|
1078
|
+
passed: existsSync8(join9(cwd, "tsconfig.json"))
|
|
825
1079
|
}
|
|
826
1080
|
];
|
|
827
1081
|
const sep = "\u2500".repeat(52);
|
|
@@ -848,12 +1102,12 @@ async function doctorCommand(cwd = process.cwd()) {
|
|
|
848
1102
|
}
|
|
849
1103
|
|
|
850
1104
|
// src/auto-import/generator.ts
|
|
851
|
-
import { mkdirSync as
|
|
852
|
-
import { join as
|
|
1105
|
+
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
1106
|
+
import { join as join11 } from "path";
|
|
853
1107
|
|
|
854
1108
|
// src/auto-import/scanner.ts
|
|
855
|
-
import { existsSync as
|
|
856
|
-
import { basename, extname, join as
|
|
1109
|
+
import { existsSync as existsSync9, readdirSync as readdirSync3 } from "fs";
|
|
1110
|
+
import { basename, extname, join as join10, relative as relative3 } from "path";
|
|
857
1111
|
var DEFAULT_EXTENSIONS = [
|
|
858
1112
|
".ts",
|
|
859
1113
|
".tsx",
|
|
@@ -884,12 +1138,12 @@ function toPascalCase(text) {
|
|
|
884
1138
|
return text.replace(/[-_.]/g, " ").replace(/\s+(.)/g, (_match, char) => char.toUpperCase()).replace(/^(.)/, (_match, char) => char.toUpperCase());
|
|
885
1139
|
}
|
|
886
1140
|
function deriveExportName(filePath, dirPath) {
|
|
887
|
-
const relativePath =
|
|
1141
|
+
const relativePath = relative3(dirPath, filePath).replace(/\\/g, "/");
|
|
888
1142
|
const withoutExtension = relativePath.replace(/\.(ts|tsx|js|jsx)$/, "").replace(/\/index$/, "");
|
|
889
1143
|
return toPascalCase(withoutExtension.replace(/\//g, "_"));
|
|
890
1144
|
}
|
|
891
1145
|
function walkDirectory(dirPath, extensions) {
|
|
892
|
-
if (!
|
|
1146
|
+
if (!existsSync9(dirPath)) {
|
|
893
1147
|
return [];
|
|
894
1148
|
}
|
|
895
1149
|
const collected = [];
|
|
@@ -901,7 +1155,7 @@ function walkDirectory(dirPath, extensions) {
|
|
|
901
1155
|
if (entry.name.startsWith(".") || entry.name === "node_modules") {
|
|
902
1156
|
continue;
|
|
903
1157
|
}
|
|
904
|
-
const fullPath =
|
|
1158
|
+
const fullPath = join10(currentPath, entry.name);
|
|
905
1159
|
if (entry.isDirectory()) {
|
|
906
1160
|
walk(fullPath);
|
|
907
1161
|
continue;
|
|
@@ -917,16 +1171,16 @@ function walkDirectory(dirPath, extensions) {
|
|
|
917
1171
|
function scanForExports(options) {
|
|
918
1172
|
const extensions = options.extensions ?? DEFAULT_EXTENSIONS;
|
|
919
1173
|
const discovered = [];
|
|
920
|
-
const outputAbs =
|
|
1174
|
+
const outputAbs = join10(options.frontendRoot, options.outputDirectory);
|
|
921
1175
|
for (const directory of options.directories) {
|
|
922
|
-
const directoryAbs =
|
|
1176
|
+
const directoryAbs = join10(options.frontendRoot, directory);
|
|
923
1177
|
const files = walkDirectory(directoryAbs, extensions);
|
|
924
1178
|
for (const filePath of files) {
|
|
925
|
-
const relativeFromOutput =
|
|
1179
|
+
const relativeFromOutput = relative3(outputAbs, filePath).replace(/\\/g, "/").replace(/\.(ts|tsx)$/, "");
|
|
926
1180
|
const importPath = relativeFromOutput.startsWith(".") ? relativeFromOutput : `./${relativeFromOutput}`;
|
|
927
1181
|
discovered.push({
|
|
928
1182
|
name: deriveExportName(filePath, directoryAbs),
|
|
929
|
-
filePath:
|
|
1183
|
+
filePath: relative3(options.frontendRoot, filePath).replace(/\\/g, "/"),
|
|
930
1184
|
importPath,
|
|
931
1185
|
kind: detectKind(filePath)
|
|
932
1186
|
});
|
|
@@ -959,17 +1213,17 @@ function generateAutoImports(options) {
|
|
|
959
1213
|
extensions: options.extensions
|
|
960
1214
|
} : {}
|
|
961
1215
|
});
|
|
962
|
-
const outputDir =
|
|
963
|
-
|
|
1216
|
+
const outputDir = join11(options.frontendRoot, options.outputDirectory);
|
|
1217
|
+
mkdirSync3(outputDir, {
|
|
964
1218
|
recursive: true
|
|
965
1219
|
});
|
|
966
1220
|
const tsContent = `${GENERATED_HEADER}
|
|
967
1221
|
${buildImportLines(exports)}
|
|
968
1222
|
`;
|
|
969
|
-
|
|
1223
|
+
writeFileSync3(join11(outputDir, "auto-imports.ts"), tsContent, "utf-8");
|
|
970
1224
|
if (options.generateDts) {
|
|
971
1225
|
const dtsContent = tsContent.replace("// This file is auto-generated by: bun rakta imports:generate", "// Type declarations \u2014 auto-generated by: bun rakta imports:generate");
|
|
972
|
-
|
|
1226
|
+
writeFileSync3(join11(outputDir, "auto-imports.d.ts"), dtsContent, "utf-8");
|
|
973
1227
|
}
|
|
974
1228
|
return {
|
|
975
1229
|
generatedAt: new Date().toISOString(),
|
|
@@ -1011,18 +1265,18 @@ async function importsGenerateCommand(cwd = process.cwd()) {
|
|
|
1011
1265
|
}
|
|
1012
1266
|
|
|
1013
1267
|
// src/cli/make.ts
|
|
1014
|
-
import { existsSync as
|
|
1015
|
-
import { dirname, join as
|
|
1268
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
1269
|
+
import { dirname as dirname2, join as join12 } from "path";
|
|
1016
1270
|
function toPascalCase2(name) {
|
|
1017
1271
|
return name.split(/[-_/]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
1018
1272
|
}
|
|
1019
1273
|
function writeIfNew(filePath, content) {
|
|
1020
|
-
if (
|
|
1274
|
+
if (existsSync10(filePath)) {
|
|
1021
1275
|
console.warn(` Already exists: ${filePath}`);
|
|
1022
1276
|
return;
|
|
1023
1277
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
1278
|
+
mkdirSync4(dirname2(filePath), { recursive: true });
|
|
1279
|
+
writeFileSync4(filePath, content, "utf-8");
|
|
1026
1280
|
console.log(` Created: ${filePath}`);
|
|
1027
1281
|
}
|
|
1028
1282
|
function getRelativeFilePath(filePath, cwd) {
|
|
@@ -1030,11 +1284,11 @@ function getRelativeFilePath(filePath, cwd) {
|
|
|
1030
1284
|
}
|
|
1031
1285
|
async function makeCommand(target, name, cwd = process.cwd()) {
|
|
1032
1286
|
const config = await loadConfig(cwd);
|
|
1033
|
-
const appDir =
|
|
1287
|
+
const appDir = join12(cwd, config.appDir);
|
|
1034
1288
|
const componentName = toPascalCase2(name);
|
|
1035
1289
|
switch (target) {
|
|
1036
1290
|
case "page": {
|
|
1037
|
-
const filePath =
|
|
1291
|
+
const filePath = join12(appDir, name, "page.tsx");
|
|
1038
1292
|
writeIfNew(filePath, `// ${getRelativeFilePath(filePath, cwd)}
|
|
1039
1293
|
import React from "react";
|
|
1040
1294
|
import type { Metadata } from "rakta/seo";
|
|
@@ -1054,7 +1308,7 @@ export default function ${componentName}Page() {
|
|
|
1054
1308
|
break;
|
|
1055
1309
|
}
|
|
1056
1310
|
case "layout": {
|
|
1057
|
-
const filePath =
|
|
1311
|
+
const filePath = join12(appDir, name, "layout.tsx");
|
|
1058
1312
|
writeIfNew(filePath, `// ${getRelativeFilePath(filePath, cwd)}
|
|
1059
1313
|
import React from "react";
|
|
1060
1314
|
import type { LayoutProps } from "rakta/router";
|
|
@@ -1066,7 +1320,7 @@ export default function ${componentName}Layout({ children }: LayoutProps) {
|
|
|
1066
1320
|
break;
|
|
1067
1321
|
}
|
|
1068
1322
|
case "component": {
|
|
1069
|
-
const filePath =
|
|
1323
|
+
const filePath = join12(cwd, "components", "ui", `${componentName}.tsx`);
|
|
1070
1324
|
writeIfNew(filePath, `// components/ui/${componentName}.tsx
|
|
1071
1325
|
import React from "react";
|
|
1072
1326
|
|
|
@@ -1084,7 +1338,7 @@ export default ${componentName};
|
|
|
1084
1338
|
break;
|
|
1085
1339
|
}
|
|
1086
1340
|
case "api": {
|
|
1087
|
-
const filePath =
|
|
1341
|
+
const filePath = join12(appDir, "api", name, "route.ts");
|
|
1088
1342
|
writeIfNew(filePath, `// ${getRelativeFilePath(filePath, cwd)}
|
|
1089
1343
|
import type { RouteContext } from "rakta/router";
|
|
1090
1344
|
|
|
@@ -1098,23 +1352,23 @@ export async function GET(_request: Request, context: RouteContext): Promise<Res
|
|
|
1098
1352
|
}
|
|
1099
1353
|
|
|
1100
1354
|
// src/cli/routes.ts
|
|
1101
|
-
import { join as
|
|
1355
|
+
import { join as join13 } from "path";
|
|
1102
1356
|
async function routesCommand(cwd = process.cwd()) {
|
|
1103
1357
|
const config = await loadConfig(cwd);
|
|
1104
|
-
const appDir =
|
|
1358
|
+
const appDir = join13(cwd, config.appDir);
|
|
1105
1359
|
const manifest = generateManifest(appDir);
|
|
1106
1360
|
printManifest(manifest);
|
|
1107
1361
|
}
|
|
1108
1362
|
|
|
1109
1363
|
// src/cli/rpcTypes.ts
|
|
1110
|
-
import { existsSync as
|
|
1111
|
-
import { join as
|
|
1364
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
1365
|
+
import { join as join14 } from "path";
|
|
1112
1366
|
async function rpcTypesCommand(cwd = process.cwd()) {
|
|
1113
|
-
const sharedTypesDir =
|
|
1114
|
-
if (!
|
|
1115
|
-
|
|
1367
|
+
const sharedTypesDir = join14(cwd, "..", "shared", "types");
|
|
1368
|
+
if (!existsSync11(sharedTypesDir)) {
|
|
1369
|
+
mkdirSync5(sharedTypesDir, { recursive: true });
|
|
1116
1370
|
}
|
|
1117
|
-
const outputPath =
|
|
1371
|
+
const outputPath = join14(sharedTypesDir, "rpc.ts");
|
|
1118
1372
|
const content = `// shared/types/rpc.ts
|
|
1119
1373
|
// Auto-generated by: bun rakta rpc:types
|
|
1120
1374
|
// Re-exports AppRouter type from the backend for type-safe frontend RPC calls.
|
|
@@ -1122,7 +1376,7 @@ async function rpcTypesCommand(cwd = process.cwd()) {
|
|
|
1122
1376
|
|
|
1123
1377
|
export type { AppRouter } from "../../backend/src/rpc/router.js";
|
|
1124
1378
|
`;
|
|
1125
|
-
|
|
1379
|
+
writeFileSync5(outputPath, content, "utf-8");
|
|
1126
1380
|
console.log(`
|
|
1127
1381
|
Generated: shared/types/rpc.ts
|
|
1128
1382
|
`);
|
|
@@ -1132,16 +1386,16 @@ export type { AppRouter } from "../../backend/src/rpc/router.js";
|
|
|
1132
1386
|
}
|
|
1133
1387
|
|
|
1134
1388
|
// src/cli/seo.ts
|
|
1135
|
-
import { existsSync as
|
|
1136
|
-
import { join as
|
|
1389
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
1390
|
+
import { join as join15 } from "path";
|
|
1137
1391
|
async function seoGenerateCommand(cwd = process.cwd()) {
|
|
1138
1392
|
const config = await loadConfig(cwd);
|
|
1139
|
-
const appDir =
|
|
1140
|
-
const sitemapDir =
|
|
1141
|
-
const robotsDir =
|
|
1142
|
-
if (!
|
|
1143
|
-
|
|
1144
|
-
|
|
1393
|
+
const appDir = join15(cwd, config.appDir);
|
|
1394
|
+
const sitemapDir = join15(appDir, "api", "sitemap.xml");
|
|
1395
|
+
const robotsDir = join15(appDir, "api", "robots.txt");
|
|
1396
|
+
if (!existsSync12(sitemapDir)) {
|
|
1397
|
+
mkdirSync6(sitemapDir, { recursive: true });
|
|
1398
|
+
writeFileSync6(join15(sitemapDir, "route.ts"), `// app/api/sitemap.xml/route.ts \u2014 generated by: bun rakta seo:generate
|
|
1145
1399
|
import { createSitemapHandler } from "rakta/seo";
|
|
1146
1400
|
|
|
1147
1401
|
export const GET = createSitemapHandler({
|
|
@@ -1156,9 +1410,9 @@ export const GET = createSitemapHandler({
|
|
|
1156
1410
|
} else {
|
|
1157
1411
|
console.warn(" Already exists: app/api/sitemap.xml/route.ts");
|
|
1158
1412
|
}
|
|
1159
|
-
if (!
|
|
1160
|
-
|
|
1161
|
-
|
|
1413
|
+
if (!existsSync12(robotsDir)) {
|
|
1414
|
+
mkdirSync6(robotsDir, { recursive: true });
|
|
1415
|
+
writeFileSync6(join15(robotsDir, "route.ts"), `// app/api/robots.txt/route.ts \u2014 generated by: bun rakta seo:generate
|
|
1162
1416
|
import { createRobotsHandler } from "rakta/seo";
|
|
1163
1417
|
|
|
1164
1418
|
export const GET = createRobotsHandler({
|
|
@@ -1182,11 +1436,11 @@ export const GET = createRobotsHandler({
|
|
|
1182
1436
|
}
|
|
1183
1437
|
|
|
1184
1438
|
// src/cli/start.ts
|
|
1185
|
-
import { join as
|
|
1439
|
+
import { join as join17 } from "path";
|
|
1186
1440
|
|
|
1187
1441
|
// src/tide/adapter.ts
|
|
1188
|
-
import { existsSync as
|
|
1189
|
-
import { join as
|
|
1442
|
+
import { existsSync as existsSync13, readFileSync as readFileSync3, statSync as statSync4 } from "fs";
|
|
1443
|
+
import { join as join16 } from "path";
|
|
1190
1444
|
// src/tide/runtime.ts
|
|
1191
1445
|
function createRuntimeContext(request, url, params, resolvedMode) {
|
|
1192
1446
|
const searchParams = {};
|
|
@@ -1257,7 +1511,7 @@ function normalizeStaticPath(pathname) {
|
|
|
1257
1511
|
return "index.html";
|
|
1258
1512
|
}
|
|
1259
1513
|
function isReadableFile2(filePath) {
|
|
1260
|
-
return
|
|
1514
|
+
return existsSync13(filePath) && statSync4(filePath).isFile();
|
|
1261
1515
|
}
|
|
1262
1516
|
function isApiRouteExports(value) {
|
|
1263
1517
|
if (typeof value !== "object" || value === null) {
|
|
@@ -1298,7 +1552,7 @@ function createBunAdapter(adapterConfig, renderConfig) {
|
|
|
1298
1552
|
const { pathname } = url;
|
|
1299
1553
|
const staticPathname = normalizeStaticPath(pathname);
|
|
1300
1554
|
for (const searchDirectory of searchDirectories) {
|
|
1301
|
-
const filePath =
|
|
1555
|
+
const filePath = join16(searchDirectory, staticPathname);
|
|
1302
1556
|
if (isReadableFile2(filePath)) {
|
|
1303
1557
|
return new Response(readFileSync3(filePath), {
|
|
1304
1558
|
headers: {
|
|
@@ -1311,7 +1565,7 @@ function createBunAdapter(adapterConfig, renderConfig) {
|
|
|
1311
1565
|
const apiRoutes = manifest.routes.filter((route) => route.kind === "api");
|
|
1312
1566
|
const apiMatch = matchRoute(pathname, apiRoutes);
|
|
1313
1567
|
if (apiMatch) {
|
|
1314
|
-
const modulePath =
|
|
1568
|
+
const modulePath = join16(adapterConfig.appDir, apiMatch.entry.filePath);
|
|
1315
1569
|
const routeModule = await import(modulePath);
|
|
1316
1570
|
if (!isApiRouteExports(routeModule)) {
|
|
1317
1571
|
return new Response("Invalid API route module", {
|
|
@@ -1338,7 +1592,7 @@ function createBunAdapter(adapterConfig, renderConfig) {
|
|
|
1338
1592
|
}, {
|
|
1339
1593
|
appName: adapterConfig.appName,
|
|
1340
1594
|
scriptPath: "/app.js",
|
|
1341
|
-
cssPath: "/
|
|
1595
|
+
cssPath: "/app.css",
|
|
1342
1596
|
lang: "en"
|
|
1343
1597
|
});
|
|
1344
1598
|
if (renderResult.kind === "failure") {
|
|
@@ -1378,9 +1632,9 @@ async function startCommand(cwd = process.cwd()) {
|
|
|
1378
1632
|
port: projectConfig.port,
|
|
1379
1633
|
host: projectConfig.server.hostname ?? "0.0.0.0",
|
|
1380
1634
|
appName: projectConfig.appName,
|
|
1381
|
-
appDir:
|
|
1382
|
-
publicDir:
|
|
1383
|
-
outDir:
|
|
1635
|
+
appDir: join17(cwd, projectConfig.appDir),
|
|
1636
|
+
publicDir: join17(cwd, projectConfig.publicDir),
|
|
1637
|
+
outDir: join17(cwd, outputDirectory)
|
|
1384
1638
|
}, projectConfig.render);
|
|
1385
1639
|
await serverAdapter.start();
|
|
1386
1640
|
}
|
|
@@ -1446,7 +1700,7 @@ async function runForgeInspect() {
|
|
|
1446
1700
|
const projectConfig = await loadConfig(cwd);
|
|
1447
1701
|
const outputDirectory = projectConfig.build.outDir ?? "dist";
|
|
1448
1702
|
const inspectReport = inspectBuild({
|
|
1449
|
-
outDir:
|
|
1703
|
+
outDir: join18(cwd, outputDirectory),
|
|
1450
1704
|
renderConfig: projectConfig.render
|
|
1451
1705
|
});
|
|
1452
1706
|
printInspectReport(inspectReport);
|
|
@@ -1514,4 +1768,4 @@ ${BOLD}${RED}Rakta.js error:${RESET} ${errorMessage}
|
|
|
1514
1768
|
process.exit(1);
|
|
1515
1769
|
});
|
|
1516
1770
|
|
|
1517
|
-
//# debugId=
|
|
1771
|
+
//# debugId=A7E4DE422B721CBF64756E2164756E21
|