rwsdk 1.0.0-beta.1 → 1.0.0-beta.10

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.
Files changed (42) hide show
  1. package/dist/lib/e2e/constants.d.mts +13 -0
  2. package/dist/lib/e2e/constants.mjs +67 -0
  3. package/dist/lib/e2e/environment.d.mts +1 -1
  4. package/dist/lib/e2e/environment.mjs +16 -6
  5. package/dist/lib/e2e/index.d.mts +1 -0
  6. package/dist/lib/e2e/index.mjs +1 -0
  7. package/dist/lib/e2e/testHarness.d.mts +33 -3
  8. package/dist/lib/e2e/testHarness.mjs +181 -109
  9. package/dist/runtime/client/client.d.ts +1 -0
  10. package/dist/runtime/client/client.js +2 -0
  11. package/dist/runtime/client/navigation.d.ts +8 -0
  12. package/dist/runtime/client/navigation.js +39 -31
  13. package/dist/runtime/entries/clientSSR.d.ts +1 -0
  14. package/dist/runtime/entries/clientSSR.js +3 -0
  15. package/dist/runtime/lib/db/createDb.d.ts +1 -2
  16. package/dist/runtime/lib/db/createDb.js +4 -0
  17. package/dist/runtime/lib/manifest.d.ts +1 -1
  18. package/dist/runtime/lib/manifest.js +7 -4
  19. package/dist/runtime/lib/realtime/client.js +8 -2
  20. package/dist/runtime/lib/router.d.ts +1 -19
  21. package/dist/runtime/lib/router.test.js +2 -0
  22. package/dist/runtime/lib/{rwContext.d.ts → types.d.ts} +1 -0
  23. package/dist/runtime/render/renderDocumentHtmlStream.d.ts +1 -1
  24. package/dist/runtime/render/renderToStream.d.ts +1 -1
  25. package/dist/runtime/render/renderToString.d.ts +1 -1
  26. package/dist/runtime/requestInfo/types.d.ts +1 -1
  27. package/dist/runtime/script.d.ts +1 -3
  28. package/dist/runtime/script.js +1 -10
  29. package/dist/runtime/worker.js +25 -0
  30. package/dist/scripts/addon.mjs +1 -1
  31. package/dist/scripts/smoke-test.mjs +4 -2
  32. package/dist/scripts/worker-run.d.mts +1 -1
  33. package/dist/scripts/worker-run.mjs +50 -113
  34. package/dist/vite/buildApp.mjs +2 -0
  35. package/dist/vite/directiveModulesDevPlugin.mjs +1 -1
  36. package/dist/vite/linkerPlugin.mjs +1 -1
  37. package/dist/vite/redwoodPlugin.mjs +0 -4
  38. package/dist/vite/runDirectivesScan.mjs +57 -12
  39. package/package.json +9 -7
  40. package/dist/vite/manifestPlugin.d.mts +0 -4
  41. package/dist/vite/manifestPlugin.mjs +0 -63
  42. /package/dist/runtime/lib/{rwContext.js → types.js} +0 -0
@@ -4,6 +4,7 @@ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
4
4
  // context(justinvdm, 14 Aug 2025): `react-server-dom-webpack` uses this global
5
5
  // to load modules, so we need to define it here before importing
6
6
  // "react-server-dom-webpack."
7
+ // prettier-ignore
7
8
  import "./setWebpackRequire";
8
9
  import React from "react";
9
10
  import { hydrateRoot } from "react-dom/client";
@@ -11,6 +12,7 @@ import { createFromFetch, createFromReadableStream, encodeReply, } from "react-s
11
12
  import { rscStream } from "rsc-html-stream/client";
12
13
  export { default as React } from "react";
13
14
  export { ClientOnly } from "./ClientOnly.js";
15
+ export { initClientNavigation, navigate } from "./navigation.js";
14
16
  export const fetchTransport = (transportContext) => {
15
17
  const fetchCallServer = async (id, args) => {
16
18
  const url = new URL(window.location.href);
@@ -4,6 +4,14 @@ export interface ClientNavigationOptions {
4
4
  scrollBehavior?: "auto" | "smooth" | "instant";
5
5
  }
6
6
  export declare function validateClickEvent(event: MouseEvent, target: HTMLElement): boolean;
7
+ export interface NavigateOptions {
8
+ history?: "push" | "replace";
9
+ info?: {
10
+ scrollToTop?: boolean;
11
+ scrollBehavior?: "auto" | "smooth" | "instant";
12
+ };
13
+ }
14
+ export declare function navigate(href: string, options?: NavigateOptions): Promise<void>;
7
15
  export declare function initClientNavigation(opts?: ClientNavigationOptions): {
8
16
  handleResponse: (response: Response) => boolean;
9
17
  };
@@ -1,10 +1,3 @@
1
- function saveScrollPosition(x, y) {
2
- window.history.replaceState({
3
- ...window.history.state,
4
- scrollX: x,
5
- scrollY: y,
6
- }, "", window.location.href);
7
- }
8
1
  export function validateClickEvent(event, target) {
9
2
  // should this only work for left click?
10
3
  if (event.button !== 0) {
@@ -37,19 +30,44 @@ export function validateClickEvent(event, target) {
37
30
  }
38
31
  return true;
39
32
  }
33
+ let IS_CLIENT_NAVIGATION = false;
34
+ export async function navigate(href, options = { history: "push" }) {
35
+ if (!IS_CLIENT_NAVIGATION) {
36
+ window.location.href = href;
37
+ return;
38
+ }
39
+ saveScrollPosition(window.scrollX, window.scrollY);
40
+ const url = window.location.origin + href;
41
+ if (options.history === "push") {
42
+ window.history.pushState({ path: href }, "", url);
43
+ }
44
+ else {
45
+ window.history.replaceState({ path: href }, "", url);
46
+ }
47
+ // @ts-expect-error
48
+ await globalThis.__rsc_callServer();
49
+ const scrollToTop = options.info?.scrollToTop ?? true;
50
+ const scrollBehavior = options.info?.scrollBehavior ?? "instant";
51
+ if (scrollToTop && history.scrollRestoration === "auto") {
52
+ window.scrollTo({
53
+ top: 0,
54
+ left: 0,
55
+ behavior: scrollBehavior,
56
+ });
57
+ saveScrollPosition(0, 0);
58
+ }
59
+ }
60
+ function saveScrollPosition(x, y) {
61
+ window.history.replaceState({
62
+ ...window.history.state,
63
+ scrollX: x,
64
+ scrollY: y,
65
+ }, "", window.location.href);
66
+ }
40
67
  export function initClientNavigation(opts = {}) {
41
- const options = {
42
- onNavigate: async function onNavigate() {
43
- // @ts-expect-error
44
- await globalThis.__rsc_callServer();
45
- },
46
- scrollToTop: true,
47
- scrollBehavior: "instant",
48
- ...opts,
49
- };
68
+ IS_CLIENT_NAVIGATION = true;
50
69
  history.scrollRestoration = "auto";
51
70
  document.addEventListener("click", async function handleClickEvent(event) {
52
- // Prevent default navigation
53
71
  if (!validateClickEvent(event, event.target)) {
54
72
  return;
55
73
  }
@@ -57,28 +75,18 @@ export function initClientNavigation(opts = {}) {
57
75
  const el = event.target;
58
76
  const a = el.closest("a");
59
77
  const href = a?.getAttribute("href");
60
- saveScrollPosition(window.scrollX, window.scrollY);
61
- window.history.pushState({ path: href }, "", window.location.origin + href);
62
- await options.onNavigate();
63
- if (options.scrollToTop && history.scrollRestoration === "auto") {
64
- window.scrollTo({
65
- top: 0,
66
- left: 0,
67
- behavior: options.scrollBehavior,
68
- });
69
- saveScrollPosition(0, 0);
70
- }
71
- history.scrollRestoration = "auto";
78
+ navigate(href);
72
79
  }, true);
73
80
  window.addEventListener("popstate", async function handlePopState() {
74
- saveScrollPosition(window.scrollX, window.scrollY);
75
- await options.onNavigate();
81
+ // @ts-expect-error
82
+ await globalThis.__rsc_callServer();
76
83
  });
77
84
  // Return a handleResponse function for use with initClient
78
85
  return {
79
86
  handleResponse: function handleResponse(response) {
80
87
  if (!response.ok) {
81
88
  // Redirect to the current page (window.location) to show the error
89
+ // This means the page that produced the error is called twice.
82
90
  window.location.href = window.location.href;
83
91
  return false;
84
92
  }
@@ -1,2 +1,3 @@
1
1
  import "./types/ssr";
2
2
  export * from "../lib/streams/consumeEventStream";
3
+ export declare const navigate: () => void;
@@ -1,2 +1,5 @@
1
1
  import "./types/ssr";
2
2
  export * from "../lib/streams/consumeEventStream";
3
+ export const navigate = () => {
4
+ /* stub */
5
+ };
@@ -1,3 +1,2 @@
1
1
  import { Kysely } from "kysely";
2
- import { type SqliteDurableObject } from "./index.js";
3
- export declare function createDb<T>(durableObjectBinding: DurableObjectNamespace<SqliteDurableObject>, name?: string): Kysely<T>;
2
+ export declare function createDb<DatabaseType>(durableObjectBinding: DurableObjectNamespace<any>, name?: string): Kysely<DatabaseType>;
@@ -5,6 +5,10 @@ export function createDb(durableObjectBinding, name = "main") {
5
5
  dialect: new DOWorkerDialect({
6
6
  kyselyExecuteQuery: (...args) => {
7
7
  const durableObjectId = durableObjectBinding.idFromName(name);
8
+ // context(justinvdm, 2 Oct 2025): First prize would be a type parameter
9
+ // for the durable object and then use it for `durableObjectBinding`'s
10
+ // type, rather than casting like this. However, that would prevent
11
+ // users from being able to do createDb<InferredDbType> then though.
8
12
  const stub = durableObjectBinding.get(durableObjectId);
9
13
  stub.initialize();
10
14
  return stub.kyselyExecuteQuery(...args);
@@ -8,4 +8,4 @@ export interface ManifestChunk {
8
8
  css?: string[];
9
9
  assets?: string[];
10
10
  }
11
- export declare const getManifest: () => Promise<Manifest>;
11
+ export declare function getManifest(): Promise<Manifest>;
@@ -1,14 +1,17 @@
1
1
  let manifest;
2
- export const getManifest = async () => {
2
+ export async function getManifest() {
3
3
  if (manifest) {
4
4
  return manifest;
5
5
  }
6
6
  if (import.meta.env.VITE_IS_DEV_SERVER) {
7
+ // In dev, there's no manifest, so we can use an empty object.
7
8
  manifest = {};
8
9
  }
9
10
  else {
10
- const { default: prodManifest } = await import("virtual:rwsdk:manifest.js");
11
- manifest = prodManifest;
11
+ // context(justinvdm, 2 Oct 2025): In production, the manifest is a
12
+ // placeholder string that will be replaced by the linker plugin with the
13
+ // actual manifest JSON object.
14
+ manifest = "__RWSDK_MANIFEST_PLACEHOLDER__";
12
15
  }
13
16
  return manifest;
14
- };
17
+ }
@@ -1,7 +1,13 @@
1
- import { createFromReadableStream } from "react-server-dom-webpack/client.browser";
1
+ // context(justinvdm, 14 Aug 2025): `react-server-dom-webpack` uses this globa ___webpack_require__ global,
2
+ // so we need to import our client entry point (which sets it), before importing
3
+ // prettier-ignore
2
4
  import { initClient } from "../../client/client";
3
- import { packMessage, unpackMessage, } from "./protocol";
5
+ // prettier-ignore
6
+ import { createFromReadableStream } from "react-server-dom-webpack/client.browser";
7
+ // prettier-ignore
4
8
  import { MESSAGE_TYPE } from "./shared";
9
+ // prettier-ignore
10
+ import { packMessage, unpackMessage, } from "./protocol";
5
11
  const DEFAULT_KEY = "default";
6
12
  export const initRealtimeClient = ({ key = DEFAULT_KEY, handleResponse, } = {}) => {
7
13
  const transport = realtimeTransport({ key, handleResponse });
@@ -1,24 +1,6 @@
1
- import type { Kysely } from "kysely";
2
1
  import React from "react";
3
2
  import { RequestInfo } from "../requestInfo/types";
4
- export type DocumentProps<T extends RequestInfo = RequestInfo> = T & {
5
- children: React.ReactNode;
6
- };
7
- export type LayoutProps<T extends RequestInfo = RequestInfo> = {
8
- children?: React.ReactNode;
9
- requestInfo?: T;
10
- };
11
- export type RwContext = {
12
- nonce: string;
13
- Document: React.FC<DocumentProps<any>>;
14
- rscPayload: boolean;
15
- ssr: boolean;
16
- layouts?: React.FC<LayoutProps<any>>[];
17
- databases: Map<string, Kysely<any>>;
18
- scriptsToBeLoaded: Set<string>;
19
- pageRouteResolved: PromiseWithResolvers<void> | undefined;
20
- actionResult?: unknown;
21
- };
3
+ import type { DocumentProps, LayoutProps } from "./types.js";
22
4
  export type RouteMiddleware<T extends RequestInfo = RequestInfo> = (requestInfo: T) => MaybePromise<React.JSX.Element | Response | void>;
23
5
  type RouteFunction<T extends RequestInfo = RequestInfo> = (requestInfo: T) => MaybePromise<Response>;
24
6
  type MaybePromise<T> = T | Promise<T>;
@@ -71,6 +71,8 @@ describe("defineRoutes - Request Handling Behavior", () => {
71
71
  ssr: true,
72
72
  databases: new Map(),
73
73
  scriptsToBeLoaded: new Set(),
74
+ entryScripts: new Set(),
75
+ inlineScripts: new Set(),
74
76
  pageRouteResolved: undefined,
75
77
  },
76
78
  cf: {},
@@ -1,4 +1,5 @@
1
1
  import { type Kysely } from "kysely";
2
+ import React from "react";
2
3
  import { type RequestInfo } from "../requestInfo/types.js";
3
4
  export type RwContext = {
4
5
  nonce: string;
@@ -1,4 +1,4 @@
1
- import { type DocumentProps } from "../lib/router.js";
1
+ import { type DocumentProps } from "../lib/types.js";
2
2
  import { type RequestInfo } from "../requestInfo/types.js";
3
3
  export declare const renderDocumentHtmlStream: ({ rscPayloadStream, Document, requestInfo, shouldSSR, onError, }: {
4
4
  rscPayloadStream: ReadableStream;
@@ -1,5 +1,5 @@
1
1
  import { FC, ReactElement } from "react";
2
- import { DocumentProps } from "../lib/router";
2
+ import { DocumentProps } from "../lib/types.js";
3
3
  export interface RenderToStreamOptions {
4
4
  Document?: FC<DocumentProps>;
5
5
  ssr?: boolean;
@@ -1,5 +1,5 @@
1
1
  import { FC, ReactElement } from "react";
2
- import { DocumentProps } from "../lib/router";
2
+ import { DocumentProps } from "../lib/types.js";
3
3
  export interface RenderToStringOptions {
4
4
  Document?: FC<DocumentProps>;
5
5
  injectRSCPayload?: boolean;
@@ -1,4 +1,4 @@
1
- import { RwContext } from "../lib/router";
1
+ import { RwContext } from "../lib/types.js";
2
2
  export interface DefaultAppContext {
3
3
  }
4
4
  export interface RequestInfo<Params = any, AppContext = DefaultAppContext> {
@@ -1,5 +1,3 @@
1
1
  export declare const defineScript: (fn: ({ env }: {
2
2
  env: Env;
3
- }) => Promise<unknown>) => {
4
- fetch: (request: Request, env: Env, cf: ExecutionContext) => Promise<Response>;
5
- };
3
+ }) => Promise<unknown>) => () => Promise<unknown>;
@@ -1,11 +1,2 @@
1
1
  import { env } from "cloudflare:workers";
2
- import { defineApp } from "./worker";
3
- export const defineScript = (fn) => {
4
- const app = defineApp([
5
- async () => {
6
- await fn({ env: env });
7
- return new Response("Done!");
8
- },
9
- ]);
10
- return app;
11
- };
2
+ export const defineScript = (fn) => () => fn({ env: env });
@@ -23,6 +23,29 @@ export const defineApp = (routes) => {
23
23
  url.pathname = url.pathname.slice("/assets/".length);
24
24
  return env.ASSETS.fetch(new Request(url.toString(), request));
25
25
  }
26
+ else if (import.meta.env.VITE_IS_DEV_SERVER &&
27
+ new URL(request.url).pathname === "/__worker-run") {
28
+ const url = new URL(request.url);
29
+ const scriptPath = url.searchParams.get("script");
30
+ if (!scriptPath) {
31
+ return new Response("Missing 'script' query parameter", {
32
+ status: 400,
33
+ });
34
+ }
35
+ try {
36
+ const scriptModule = await import(/* @vite-ignore */ scriptPath);
37
+ if (scriptModule.default) {
38
+ await scriptModule.default(request, env, cf);
39
+ }
40
+ return new Response("Script executed successfully");
41
+ }
42
+ catch (e) {
43
+ console.error(`Error executing script: ${scriptPath}\n\n${e.stack}`);
44
+ return new Response(`Error executing script: ${e.message}`, {
45
+ status: 500,
46
+ });
47
+ }
48
+ }
26
49
  else if (import.meta.env.VITE_IS_DEV_SERVER &&
27
50
  request.url.includes("/__vite_preamble__")) {
28
51
  return new Response('import RefreshRuntime from "/@react-refresh"; RefreshRuntime.injectIntoGlobalHook(window); window.$RefreshReg$ = () => {}; window.$RefreshSig$ = () => (type) => type; window.__vite_plugin_react_preamble_installed__ = true;', {
@@ -43,6 +66,8 @@ export const defineApp = (routes) => {
43
66
  ssr: true,
44
67
  databases: new Map(),
45
68
  scriptsToBeLoaded: new Set(),
69
+ entryScripts: new Set(),
70
+ inlineScripts: new Set(),
46
71
  pageRouteResolved: undefined,
47
72
  };
48
73
  const userResponseInit = {
@@ -35,7 +35,7 @@ export const addon = async () => {
35
35
  console.error('Could not find "rwsdk" in your dependencies or devDependencies.');
36
36
  process.exit(1);
37
37
  }
38
- if (/^\\d/.test(rwsdkVersion)) {
38
+ if (/^\d/.test(rwsdkVersion)) {
39
39
  rwsdkVersion = `v${rwsdkVersion}`;
40
40
  }
41
41
  const tmpDirPrefix = path.join(os.tmpdir(), `rwsdk-addon-${addonName}-`);
@@ -1,5 +1,5 @@
1
1
  import debug from "debug";
2
- import { join } from "path";
2
+ import { dirname, join } from "path";
3
3
  import { fileURLToPath } from "url";
4
4
  import { runSmokeTests } from "../lib/smokeTests/runSmokeTests.mjs";
5
5
  import { isRunningInCI } from "../lib/smokeTests/utils.mjs";
@@ -16,11 +16,13 @@ if (fileURLToPath(import.meta.url) === process.argv[1]) {
16
16
  // Check for CI flag first
17
17
  const ciFlag = args.includes("--ci");
18
18
  // Set initial default values (sync will be determined below)
19
+ const __dirname = dirname(fileURLToPath(import.meta.url));
20
+ const starterPath = join(__dirname, "..", "..", "..", "starter");
19
21
  const options = {
20
22
  skipDev: false,
21
23
  skipRelease: false,
22
24
  skipClient: false,
23
- projectDir: undefined,
25
+ projectDir: starterPath,
24
26
  artifactDir: join(process.cwd(), ".artifacts"), // Default to .artifacts in current directory
25
27
  keep: isRunningInCI(ciFlag), // Default to true in CI environments
26
28
  headless: true,
@@ -1 +1 @@
1
- export declare const runWorkerScript: (relativeScriptPath: string) => Promise<never>;
1
+ export {};
@@ -1,131 +1,68 @@
1
- import { Lang, parse } from "@ast-grep/napi";
2
- import baseDebug from "debug";
3
- import enhancedResolve from "enhanced-resolve";
4
- import { readFile, writeFile } from "fs/promises";
5
- import path, { resolve } from "path";
6
- import tmp from "tmp-promise";
7
- import { createServer as createViteServer } from "vite";
8
- import { unstable_readConfig } from "wrangler";
9
- import { findWranglerConfig } from "../lib/findWranglerConfig.mjs";
10
- import { redwood } from "../vite/index.mjs";
11
- const debug = baseDebug("rwsdk:worker-run");
12
- export const runWorkerScript = async (relativeScriptPath) => {
1
+ import dbg from "debug";
2
+ import getPort from "get-port";
3
+ import path from "path";
4
+ import * as vite from "vite";
5
+ import { createLogger } from "vite";
6
+ const debug = dbg("rwsdk:worker-run");
7
+ const main = async () => {
8
+ process.env.RWSDK_WORKER_RUN = "1";
9
+ const relativeScriptPath = process.argv[2];
13
10
  if (!relativeScriptPath) {
14
11
  console.error("Error: Script path is required");
15
12
  console.log("\nUsage:");
16
- console.log(" npm run worker:run <script-path>");
17
- console.log("\nOptions:");
18
- console.log(" RWSDK_WRANGLER_CONFIG Environment variable for config path");
13
+ console.log(" rwsdk worker-run <script-path>");
19
14
  console.log("\nExamples:");
20
- console.log(" npm run worker:run src/scripts/seed.ts");
21
- console.log(" RWSDK_WRANGLER_CONFIG=custom.toml npm run worker:run src/scripts/seed.ts\n");
15
+ console.log(" rwsdk worker-run src/scripts/seed.ts\n");
22
16
  process.exit(1);
23
17
  }
24
- const scriptPath = resolve(process.cwd(), relativeScriptPath);
25
- debug("Running worker script: %s", scriptPath);
26
- const workerConfigPath = process.env.RWSDK_WRANGLER_CONFIG
27
- ? resolve(process.cwd(), process.env.RWSDK_WRANGLER_CONFIG)
28
- : await findWranglerConfig(process.cwd());
29
- debug("Using wrangler config: %s", workerConfigPath);
30
- const workerConfig = unstable_readConfig({
31
- config: workerConfigPath,
32
- env: "dev",
33
- });
34
- const durableObjectsToExport = workerConfig.durable_objects?.bindings
35
- .filter((binding) => !binding.script_name)
36
- .map((binding) => binding.class_name) ?? [];
37
- const workerEntryRelativePath = workerConfig.main;
38
- const workerEntryPath = workerEntryRelativePath ?? path.join(process.cwd(), "src/worker.tsx");
39
- const durableObjectExports = [];
40
- if (durableObjectsToExport.length > 0) {
41
- const resolver = enhancedResolve.create.sync({
42
- extensions: [".mts", ".ts", ".tsx", ".mjs", ".js", ".jsx", ".json"],
43
- });
44
- const workerEntryContents = await readFile(workerEntryPath, "utf-8");
45
- const workerEntryAst = parse(Lang.Tsx, workerEntryContents);
46
- const exportDeclarations = [
47
- ...workerEntryAst.root().findAll('export { $$$EXPORTS } from "$MODULE"'),
48
- ...workerEntryAst.root().findAll("export { $$$EXPORTS } from '$MODULE'"),
49
- ...workerEntryAst.root().findAll("export { $$$EXPORTS } from '$MODULE'"),
50
- ];
51
- for (const exportDeclaration of exportDeclarations) {
52
- const moduleMatch = exportDeclaration.getMatch("MODULE");
53
- const exportsMatch = exportDeclaration.getMultipleMatches("EXPORTS");
54
- if (!moduleMatch || exportsMatch.length === 0) {
55
- continue;
56
- }
57
- const modulePath = moduleMatch.text();
58
- const specifiers = exportsMatch.map((m) => m.text().trim());
59
- for (const specifier of specifiers) {
60
- if (durableObjectsToExport.includes(specifier)) {
61
- const resolvedPath = resolver(path.dirname(workerEntryPath), modulePath);
62
- durableObjectExports.push(`export { ${specifier} } from "${resolvedPath}";`);
63
- }
64
- }
18
+ const scriptPath = path.resolve(process.cwd(), relativeScriptPath);
19
+ const port = await getPort();
20
+ let server;
21
+ const cleanup = async () => {
22
+ if (server) {
23
+ await server.close();
65
24
  }
66
- }
67
- const tmpDir = await tmp.dir({
68
- prefix: "rw-worker-run-",
69
- unsafeCleanup: true,
70
- });
71
- const relativeTmpWorkerEntryPath = "worker.tsx";
72
- const tmpWorkerPath = path.join(tmpDir.path, "wrangler.json");
73
- const tmpWorkerEntryPath = path.join(tmpDir.path, relativeTmpWorkerEntryPath);
74
- const scriptWorkerConfig = {
75
- ...workerConfig,
76
- configPath: tmpWorkerPath,
77
- userConfigPath: tmpWorkerPath,
78
- main: relativeTmpWorkerEntryPath,
25
+ process.exit();
79
26
  };
27
+ process.on("SIGINT", cleanup);
28
+ process.on("SIGTERM", cleanup);
80
29
  try {
81
- await writeFile(tmpWorkerPath, JSON.stringify(scriptWorkerConfig, null, 2));
82
- await writeFile(tmpWorkerEntryPath, `
83
- ${durableObjectExports.join("\n")}
84
- export { default } from "${scriptPath}";
85
- `);
86
- debug("Worker config written to: %s", tmpWorkerPath);
87
- debug("Worker entry written to: %s", tmpWorkerEntryPath);
88
- process.env.RWSDK_WORKER_RUN = "1";
89
- const server = await createViteServer({
90
- configFile: false,
91
- plugins: [
92
- redwood({
93
- configPath: tmpWorkerPath,
94
- includeCloudflarePlugin: true,
95
- entry: {
96
- worker: tmpWorkerEntryPath,
97
- },
98
- }),
99
- ],
30
+ server = await vite.createServer({
31
+ logLevel: "silent",
32
+ build: {
33
+ outDir: ".rwsdk",
34
+ },
35
+ customLogger: createLogger("info", {
36
+ prefix: "[rwsdk]",
37
+ allowClearScreen: true,
38
+ }),
100
39
  server: {
101
- port: 0,
40
+ port,
41
+ host: "localhost",
102
42
  },
103
43
  });
104
- debug("Vite server created");
105
- try {
106
- await server.listen();
107
- const address = server.httpServer?.address();
108
- debug("Server listening on address: %o", address);
109
- if (!address || typeof address === "string") {
110
- throw new Error("Dev server address is invalid");
44
+ await server.listen();
45
+ const url = `http://localhost:${port}/__worker-run?script=${scriptPath}`;
46
+ debug("Fetching %s", url);
47
+ const response = await fetch(url);
48
+ debug("Response from worker: %s", response);
49
+ if (!response.ok) {
50
+ const errorText = await response.text();
51
+ console.error(`Error: worker-run script failed with status ${response.status}.`);
52
+ if (errorText) {
53
+ console.error("Response:", errorText);
111
54
  }
112
- debug("Fetching worker...");
113
- await fetch(`http://localhost:${address.port}/`);
114
- debug("Worker fetched successfully");
115
- }
116
- finally {
117
- debug("Closing server...");
118
- server.close();
119
- debug("Server closed");
55
+ process.exit(1);
120
56
  }
57
+ const responseText = await response.text();
58
+ debug("Response from worker: %s", responseText);
59
+ }
60
+ catch (e) {
61
+ console.error("rwsdk: Error running script:\n\n%s", e.message);
62
+ process.exit(1);
121
63
  }
122
64
  finally {
123
- debug("Closing inspector servers...");
124
- debug("Temporary files cleaned up");
65
+ await cleanup();
125
66
  }
126
- // todo(justinvdm, 01 Apr 2025): Investigate what handles are remaining open
127
- process.exit(0);
128
67
  };
129
- if (import.meta.url === new URL(process.argv[1], import.meta.url).href) {
130
- runWorkerScript(process.argv[2]);
131
- }
68
+ main();
@@ -1,4 +1,5 @@
1
1
  import debug from "debug";
2
+ import { rm } from "node:fs/promises";
2
3
  import { resolve } from "node:path";
3
4
  import { runDirectivesScan } from "./runDirectivesScan.mjs";
4
5
  const log = debug("rwsdk:vite:build-app");
@@ -10,6 +11,7 @@ const log = debug("rwsdk:vite:build-app");
10
11
  * @see docs/architecture/productionBuildProcess.md
11
12
  */
12
13
  export async function buildApp({ builder, clientEntryPoints, clientFiles, serverFiles, projectRootDir, workerEntryPathname, }) {
14
+ await rm(resolve(projectRootDir, "dist"), { recursive: true, force: true });
13
15
  const workerEnv = builder.environments.worker;
14
16
  await runDirectivesScan({
15
17
  rootConfig: builder.config,
@@ -38,7 +38,7 @@ export const directiveModulesDevPlugin = ({ clientFiles, serverFiles, projectRoo
38
38
  return {
39
39
  name: "rwsdk:directive-modules-dev",
40
40
  configureServer(server) {
41
- if (!process.env.VITE_IS_DEV_SERVER || process.env.RWSDK_WORKER_RUN) {
41
+ if (!process.env.VITE_IS_DEV_SERVER) {
42
42
  resolveScanPromise();
43
43
  return;
44
44
  }
@@ -9,7 +9,7 @@ export function linkWorkerBundle({ code, manifestContent, projectRootDir, }) {
9
9
  const manifest = JSON.parse(manifestContent);
10
10
  // 1. Replace the manifest placeholder with the actual manifest content.
11
11
  log("Injecting manifest into worker bundle");
12
- newCode = newCode.replace('"__RWSDK_MANIFEST_PLACEHOLDER__"', manifestContent);
12
+ newCode = newCode.replace(/['"]__RWSDK_MANIFEST_PLACEHOLDER__['"]/, manifestContent);
13
13
  // 2. Replace asset placeholders with their final hashed paths.
14
14
  log("Replacing asset placeholders in final worker bundle");
15
15
  for (const [key, value] of Object.entries(manifest)) {
@@ -18,7 +18,6 @@ import { directivesPlugin } from "./directivesPlugin.mjs";
18
18
  import { injectVitePreamble } from "./injectVitePreamblePlugin.mjs";
19
19
  import { knownDepsResolverPlugin } from "./knownDepsResolverPlugin.mjs";
20
20
  import { linkerPlugin } from "./linkerPlugin.mjs";
21
- import { manifestPlugin } from "./manifestPlugin.mjs";
22
21
  import { miniflareHMRPlugin } from "./miniflareHMRPlugin.mjs";
23
22
  import { moveStaticAssetsPlugin } from "./moveStaticAssetsPlugin.mjs";
24
23
  import { prismaPlugin } from "./prismaPlugin.mjs";
@@ -144,9 +143,6 @@ export const redwoodPlugin = async (options = {}) => {
144
143
  clientEntryPoints,
145
144
  projectRootDir,
146
145
  }),
147
- manifestPlugin({
148
- projectRootDir,
149
- }),
150
146
  moveStaticAssetsPlugin({ rootDir: projectRootDir }),
151
147
  prismaPlugin({ projectRootDir }),
152
148
  linkerPlugin({ projectRootDir }),