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/index.js CHANGED
@@ -1615,8 +1615,8 @@ function mergeConfig(baseConfig, overrideConfig) {
1615
1615
  };
1616
1616
  }
1617
1617
  // src/forge/build.ts
1618
- import { mkdirSync as mkdirSync2 } from "fs";
1619
- import { join as join5, resolve as resolve2 } from "path";
1618
+ import { existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
1619
+ import { join as join6, resolve as resolve2 } from "path";
1620
1620
 
1621
1621
  // src/router/manifest.ts
1622
1622
  import { existsSync as existsSync4, readFileSync, writeFileSync as writeFileSync2 } from "fs";
@@ -1776,14 +1776,219 @@ function printManifest(manifest) {
1776
1776
  `);
1777
1777
  }
1778
1778
 
1779
+ // src/forge/clientEntry.ts
1780
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
1781
+ import { dirname, join as join5, relative as relative3 } from "path";
1782
+ function toModuleSpecifier(fromFile, targetFile) {
1783
+ const relativePath = relative3(dirname(fromFile), targetFile).replace(/\\/g, "/");
1784
+ if (relativePath.startsWith(".")) {
1785
+ return relativePath;
1786
+ }
1787
+ return `./${relativePath}`;
1788
+ }
1789
+ function toCssImportSpecifier(entryPath, projectRoot) {
1790
+ const candidates = [
1791
+ join5(projectRoot, "styles", "globals.css"),
1792
+ join5(projectRoot, "styles", "globals.scss"),
1793
+ join5(projectRoot, "styles", "globals.sass")
1794
+ ];
1795
+ const stylePath = candidates.find((candidate) => existsSync5(candidate));
1796
+ if (!stylePath) {
1797
+ return null;
1798
+ }
1799
+ return toModuleSpecifier(entryPath, stylePath);
1800
+ }
1801
+ function getPageRoutes(manifest) {
1802
+ return manifest.routes.filter((route) => route.kind === "page");
1803
+ }
1804
+ function buildRouteImports(entryPath, appDir, pageRoutes) {
1805
+ const routeEntries = pageRoutes.map((route) => {
1806
+ const pagePath = join5(appDir, route.filePath);
1807
+ return ` ${JSON.stringify(route.urlPattern)}: () => import("${toModuleSpecifier(entryPath, pagePath)}"),`;
1808
+ }).join(`
1809
+ `);
1810
+ return `const routeModules = {
1811
+ ${routeEntries}
1812
+ } as const;`;
1813
+ }
1814
+ function buildRouteTable(pageRoutes) {
1815
+ const routeEntries = pageRoutes.map((route) => ` ${JSON.stringify(route.urlPattern)}: routeModules[${JSON.stringify(route.urlPattern)}],`).join(`
1816
+ `);
1817
+ return `const routes = {
1818
+ ${routeEntries}
1819
+ } as const;`;
1820
+ }
1821
+ function findExistingModule(basePathWithoutExtension) {
1822
+ const candidates = [".tsx", ".ts", ".jsx", ".js"].map((extension) => `${basePathWithoutExtension}${extension}`);
1823
+ return candidates.find((candidate) => existsSync5(candidate));
1824
+ }
1825
+ function buildStarterGlobalLoaders(entryPath, appDir) {
1826
+ const mascotPath = findExistingModule(join5(appDir, "components", "raktaShrimpMascot"));
1827
+ const gamePath = findExistingModule(join5(appDir, "components", "shrimpRunGame"));
1828
+ const loaders = [];
1829
+ if (mascotPath !== undefined) {
1830
+ loaders.push(` const mascotModule = await import("${toModuleSpecifier(entryPath, mascotPath)}");
1831
+ (globalThis as typeof globalThis & Record<string, unknown>).RaktaShrimpMascot = mascotModule.default;`);
1832
+ }
1833
+ if (gamePath !== undefined) {
1834
+ loaders.push(` const gameModule = await import("${toModuleSpecifier(entryPath, gamePath)}");
1835
+ (globalThis as typeof globalThis & Record<string, unknown>).ShrimpRunGame = gameModule.default;`);
1836
+ }
1837
+ if (loaders.length === 0) {
1838
+ return `async function loadRaktaGlobals(): Promise<void> {
1839
+ return;
1840
+ }`;
1841
+ }
1842
+ return `async function loadRaktaGlobals(): Promise<void> {
1843
+ ${loaders.join(`
1844
+
1845
+ `)}
1846
+ }`;
1847
+ }
1848
+ function buildClientEntrySource(options, entryPath) {
1849
+ const pageRoutes = getPageRoutes(options.manifest);
1850
+ const routeModules = buildRouteImports(entryPath, options.appDir, pageRoutes);
1851
+ const routeTable = buildRouteTable(pageRoutes);
1852
+ const cssImportSpecifier = toCssImportSpecifier(entryPath, options.projectRoot);
1853
+ const cssImport = cssImportSpecifier !== null ? `import "${cssImportSpecifier}";
1854
+ ` : "";
1855
+ const starterGlobalLoaders = buildStarterGlobalLoaders(entryPath, options.appDir);
1856
+ return `import React, { useEffect, useState } from "react";
1857
+ import { createRoot } from "react-dom/client";
1858
+ import * as ReactHooks from "react";
1859
+ ${cssImport}
1860
+ (globalThis as typeof globalThis & Record<string, unknown>).useCallback = ReactHooks.useCallback;
1861
+ (globalThis as typeof globalThis & Record<string, unknown>).useEffect = ReactHooks.useEffect;
1862
+ (globalThis as typeof globalThis & Record<string, unknown>).useRef = ReactHooks.useRef;
1863
+ (globalThis as typeof globalThis & Record<string, unknown>).useState = ReactHooks.useState;
1864
+
1865
+ ${starterGlobalLoaders}
1866
+
1867
+ await loadRaktaGlobals();
1868
+
1869
+ ${routeModules}
1870
+
1871
+ ${routeTable}
1872
+
1873
+ type RoutePath = keyof typeof routes;
1874
+ type PageModule = { default: React.ComponentType };
1875
+
1876
+ function normalizePathname(pathname: string): string {
1877
+ if (pathname.length > 1 && pathname.endsWith("/")) {
1878
+ return pathname.slice(0, -1);
1879
+ }
1880
+
1881
+ return pathname;
1882
+ }
1883
+
1884
+ function resolveRouteLoader(pathname: string): () => Promise<PageModule> {
1885
+ const normalizedPathname = normalizePathname(pathname) as RoutePath;
1886
+
1887
+ return routes[normalizedPathname] ?? routes["/"];
1888
+ }
1889
+
1890
+ function navigate(to: string): void {
1891
+ window.history.pushState({ source: "rakta-click", to }, "", to);
1892
+ window.dispatchEvent(new PopStateEvent("popstate", { state: { to } }));
1893
+ }
1894
+
1895
+ function App(): React.ReactElement {
1896
+ const [pathname, setPathname] = useState(() => window.location.pathname);
1897
+ const [Page, setPage] = useState<React.ComponentType | null>(null);
1898
+
1899
+ useEffect(() => {
1900
+ function handlePopState(): void {
1901
+ setPathname(window.location.pathname);
1902
+ }
1903
+
1904
+ function handleClick(event: MouseEvent): void {
1905
+ const target = event.target;
1906
+
1907
+ if (!(target instanceof Element)) {
1908
+ return;
1909
+ }
1910
+
1911
+ const clickElement = target.closest("click");
1912
+
1913
+ if (!clickElement) {
1914
+ return;
1915
+ }
1916
+
1917
+ const to = clickElement.getAttribute("to");
1918
+
1919
+ if (!to || to.startsWith("http://") || to.startsWith("https://")) {
1920
+ return;
1921
+ }
1922
+
1923
+ event.preventDefault();
1924
+ navigate(to);
1925
+ setPathname(window.location.pathname);
1926
+ }
1927
+
1928
+ window.addEventListener("popstate", handlePopState);
1929
+ document.addEventListener("click", handleClick);
1930
+
1931
+ return () => {
1932
+ window.removeEventListener("popstate", handlePopState);
1933
+ document.removeEventListener("click", handleClick);
1934
+ };
1935
+ }, []);
1936
+
1937
+ useEffect(() => {
1938
+ let isCurrent = true;
1939
+
1940
+ resolveRouteLoader(pathname)().then((pageModule) => {
1941
+ if (isCurrent) {
1942
+ setPage(() => pageModule.default);
1943
+ }
1944
+ });
1945
+
1946
+ return () => {
1947
+ isCurrent = false;
1948
+ };
1949
+ }, [pathname]);
1950
+
1951
+ if (!Page) {
1952
+ return React.createElement("main", {
1953
+ style: {
1954
+ minHeight: "100vh",
1955
+ display: "grid",
1956
+ placeItems: "center",
1957
+ background: "#050505",
1958
+ color: "#f8fafc",
1959
+ fontFamily: "ui-sans-serif, system-ui, sans-serif",
1960
+ },
1961
+ }, "Loading Rakta.js...");
1962
+ }
1963
+
1964
+ return React.createElement(Page);
1965
+ }
1966
+
1967
+ const rootElement = document.getElementById("rakta-root");
1968
+
1969
+ if (!rootElement) {
1970
+ throw new Error("Rakta.js root element #rakta-root was not found.");
1971
+ }
1972
+
1973
+ createRoot(rootElement).render(React.createElement(App));
1974
+ `;
1975
+ }
1976
+ function writeClientEntry(options) {
1977
+ mkdirSync2(options.workDir, { recursive: true });
1978
+ const entryPath = join5(options.workDir, "client-entry.tsx");
1979
+ const entrySource = buildClientEntrySource(options, entryPath);
1980
+ writeFileSync3(entryPath, entrySource, "utf-8");
1981
+ return entryPath;
1982
+ }
1983
+
1779
1984
  // src/forge/build.ts
1780
1985
  async function buildProject(options) {
1781
1986
  const startMs = Date.now();
1782
1987
  const artifacts = [];
1783
1988
  const errors = [];
1784
- mkdirSync2(resolve2(options.outDir), { recursive: true });
1989
+ mkdirSync3(resolve2(options.outDir), { recursive: true });
1785
1990
  const manifest = generateManifest(options.appDir);
1786
- const manifestPath = join5(options.outDir, "route-manifest.json");
1991
+ const manifestPath = join6(options.outDir, "route-manifest.json");
1787
1992
  writeManifest(manifest, manifestPath);
1788
1993
  const manifestContent = JSON.stringify(manifest);
1789
1994
  artifacts.push({
@@ -1791,15 +1996,21 @@ async function buildProject(options) {
1791
1996
  sizeBytes: new TextEncoder().encode(manifestContent).byteLength,
1792
1997
  kind: "manifest"
1793
1998
  });
1999
+ const entryPoint = existsSync6(options.entryPoint) ? options.entryPoint : writeClientEntry({
2000
+ projectRoot: options.projectRoot,
2001
+ appDir: options.appDir,
2002
+ workDir: join6(options.projectRoot, ".rakta"),
2003
+ manifest
2004
+ });
1794
2005
  const buildResult = await Bun.build({
1795
- entrypoints: [options.entryPoint],
2006
+ entrypoints: [entryPoint],
1796
2007
  outdir: options.outDir,
1797
2008
  target: options.target,
1798
2009
  minify: options.minify,
1799
2010
  sourcemap: options.sourcemap ? "external" : "none",
1800
2011
  splitting: options.splitting,
1801
2012
  naming: {
1802
- entry: "[name].[ext]",
2013
+ entry: "app.[ext]",
1803
2014
  chunk: "chunks/[name]-[hash].[ext]",
1804
2015
  asset: "assets/[name]-[hash].[ext]"
1805
2016
  }
@@ -1832,8 +2043,8 @@ async function buildProject(options) {
1832
2043
  };
1833
2044
  }
1834
2045
  // src/forge/devServer.ts
1835
- import { existsSync as existsSync5, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
1836
- import { join as join6 } from "path";
2046
+ import { existsSync as existsSync7, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
2047
+ import { join as join7 } from "path";
1837
2048
 
1838
2049
  // src/render/modes.ts
1839
2050
  var RENDER_MODE_DESCRIPTORS = {
@@ -2091,18 +2302,55 @@ function resolveDevPort(port) {
2091
2302
  return port > 0 ? port : DEFAULT_DEV_PORT;
2092
2303
  }
2093
2304
  function isReadableFile(filePath) {
2094
- return existsSync5(filePath) && statSync2(filePath).isFile();
2305
+ return existsSync7(filePath) && statSync2(filePath).isFile();
2306
+ }
2307
+ async function buildDevClientBundle(options, manifest) {
2308
+ const workDir = join7(options.projectRoot, ".rakta");
2309
+ const clientOutDir = join7(workDir, "dev");
2310
+ const clientEntry = writeClientEntry({
2311
+ projectRoot: options.projectRoot,
2312
+ appDir: options.appDir,
2313
+ workDir,
2314
+ manifest
2315
+ });
2316
+ const buildResult = await Bun.build({
2317
+ entrypoints: [clientEntry],
2318
+ outdir: clientOutDir,
2319
+ target: "browser",
2320
+ sourcemap: "external",
2321
+ naming: {
2322
+ entry: "app.[ext]",
2323
+ chunk: "chunks/[name]-[hash].[ext]",
2324
+ asset: "assets/[name]-[hash].[ext]"
2325
+ }
2326
+ });
2327
+ if (!buildResult.success) {
2328
+ const buildErrors = buildResult.logs.map((buildLog) => buildLog.message).join(`
2329
+ `);
2330
+ throw new Error(`Failed to build Rakta.js client bundle.
2331
+ ${buildErrors}`);
2332
+ }
2333
+ return clientOutDir;
2095
2334
  }
2096
- function startDevServer(options) {
2335
+ async function startDevServer(options) {
2097
2336
  const manifest = generateManifest(options.appDir);
2098
2337
  const resolvedPort = resolveDevPort(options.port);
2338
+ const clientOutDir = await buildDevClientBundle(options, manifest);
2099
2339
  const server = Bun.serve({
2100
2340
  port: resolvedPort,
2101
2341
  hostname: options.host,
2102
2342
  async fetch(request) {
2103
2343
  const url = new URL(request.url);
2104
2344
  const { pathname } = url;
2105
- const publicPath = join6(options.publicDir, pathname);
2345
+ if (clientOutDir.length > 0) {
2346
+ const clientBundlePath = join7(clientOutDir, pathname);
2347
+ if (isReadableFile(clientBundlePath)) {
2348
+ return new Response(readFileSync2(clientBundlePath), {
2349
+ headers: { "Content-Type": resolveMime(clientBundlePath) }
2350
+ });
2351
+ }
2352
+ }
2353
+ const publicPath = join7(options.publicDir, pathname);
2106
2354
  if (isReadableFile(publicPath)) {
2107
2355
  return new Response(readFileSync2(publicPath), {
2108
2356
  headers: { "Content-Type": resolveMime(pathname) }
@@ -2110,7 +2358,7 @@ function startDevServer(options) {
2110
2358
  }
2111
2359
  const apiMatch = matchRoute(pathname, manifest.routes.filter((route) => route.kind === "api"));
2112
2360
  if (apiMatch) {
2113
- const modulePath = join6(options.appDir, apiMatch.entry.filePath);
2361
+ const modulePath = join7(options.appDir, apiMatch.entry.filePath);
2114
2362
  const routeModule = await import(modulePath);
2115
2363
  const method = request.method.toUpperCase();
2116
2364
  const handler = routeModule[method];
@@ -2138,7 +2386,7 @@ function startDevServer(options) {
2138
2386
  }, {
2139
2387
  appName: options.appName,
2140
2388
  scriptPath: "/app.js",
2141
- cssPath: "/globals.css",
2389
+ cssPath: "/app.css",
2142
2390
  lang: "en"
2143
2391
  });
2144
2392
  if (result.kind === "failure") {
@@ -2159,8 +2407,8 @@ function startDevServer(options) {
2159
2407
  };
2160
2408
  }
2161
2409
  // src/forge/inspect.ts
2162
- import { existsSync as existsSync6, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
2163
- import { join as join7 } from "path";
2410
+ import { existsSync as existsSync8, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
2411
+ import { join as join8 } from "path";
2164
2412
  function detectArtifactKind(filename) {
2165
2413
  if (filename === "route-manifest.json")
2166
2414
  return "manifest";
@@ -2178,13 +2426,13 @@ function formatBytes(bytes) {
2178
2426
  return `${(bytes / 1048576).toFixed(2)} MB`;
2179
2427
  }
2180
2428
  function scanDirectory2(dirPath) {
2181
- if (!existsSync6(dirPath))
2429
+ if (!existsSync8(dirPath))
2182
2430
  return [];
2183
2431
  const collected = [];
2184
2432
  function walk(current) {
2185
2433
  const entries = readdirSync3(current, { withFileTypes: true });
2186
2434
  for (const entry of entries) {
2187
- const full = join7(current, entry.name);
2435
+ const full = join8(current, entry.name);
2188
2436
  if (entry.isDirectory()) {
2189
2437
  walk(full);
2190
2438
  } else if (entry.isFile()) {
@@ -2201,7 +2449,7 @@ function scanDirectory2(dirPath) {
2201
2449
  }
2202
2450
  function inspectBuild(options) {
2203
2451
  const artifacts = scanDirectory2(options.outDir);
2204
- const manifestPath = join7(options.outDir, "route-manifest.json");
2452
+ const manifestPath = join8(options.outDir, "route-manifest.json");
2205
2453
  const manifest = readManifest(manifestPath);
2206
2454
  const emptyManifest = {
2207
2455
  version: "1",
@@ -3605,8 +3853,8 @@ function createRaktaStore(creator) {
3605
3853
  return Object.assign(useStore, storeApi);
3606
3854
  }
3607
3855
  // src/tide/adapter.ts
3608
- import { existsSync as existsSync7, readFileSync as readFileSync3, statSync as statSync4 } from "fs";
3609
- import { join as join8 } from "path";
3856
+ import { existsSync as existsSync9, readFileSync as readFileSync3, statSync as statSync4 } from "fs";
3857
+ import { join as join9 } from "path";
3610
3858
 
3611
3859
  // src/tide/runtime.ts
3612
3860
  function createRuntimeContext(request, url, params, resolvedMode) {
@@ -3678,7 +3926,7 @@ function normalizeStaticPath(pathname) {
3678
3926
  return "index.html";
3679
3927
  }
3680
3928
  function isReadableFile2(filePath) {
3681
- return existsSync7(filePath) && statSync4(filePath).isFile();
3929
+ return existsSync9(filePath) && statSync4(filePath).isFile();
3682
3930
  }
3683
3931
  function isApiRouteExports(value) {
3684
3932
  if (typeof value !== "object" || value === null) {
@@ -3719,7 +3967,7 @@ function createBunAdapter(adapterConfig, renderConfig) {
3719
3967
  const { pathname } = url;
3720
3968
  const staticPathname = normalizeStaticPath(pathname);
3721
3969
  for (const searchDirectory of searchDirectories) {
3722
- const filePath = join8(searchDirectory, staticPathname);
3970
+ const filePath = join9(searchDirectory, staticPathname);
3723
3971
  if (isReadableFile2(filePath)) {
3724
3972
  return new Response(readFileSync3(filePath), {
3725
3973
  headers: {
@@ -3732,7 +3980,7 @@ function createBunAdapter(adapterConfig, renderConfig) {
3732
3980
  const apiRoutes = manifest.routes.filter((route) => route.kind === "api");
3733
3981
  const apiMatch = matchRoute(pathname, apiRoutes);
3734
3982
  if (apiMatch) {
3735
- const modulePath = join8(adapterConfig.appDir, apiMatch.entry.filePath);
3983
+ const modulePath = join9(adapterConfig.appDir, apiMatch.entry.filePath);
3736
3984
  const routeModule = await import(modulePath);
3737
3985
  if (!isApiRouteExports(routeModule)) {
3738
3986
  return new Response("Invalid API route module", {
@@ -3759,7 +4007,7 @@ function createBunAdapter(adapterConfig, renderConfig) {
3759
4007
  }, {
3760
4008
  appName: adapterConfig.appName,
3761
4009
  scriptPath: "/app.js",
3762
- cssPath: "/globals.css",
4010
+ cssPath: "/app.css",
3763
4011
  lang: "en"
3764
4012
  });
3765
4013
  if (renderResult.kind === "failure") {
@@ -3875,4 +4123,4 @@ export {
3875
4123
  ArrayType
3876
4124
  };
3877
4125
 
3878
- //# debugId=64277F0C4350509364756E2164756E21
4126
+ //# debugId=5C94719A0BDF167E64756E2164756E21