rwsdk 0.0.83 → 0.0.84

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 (41) hide show
  1. package/dist/runtime/lib/router.d.ts +10 -1
  2. package/dist/runtime/lib/router.js +47 -13
  3. package/dist/runtime/lib/router.test.d.ts +1 -0
  4. package/dist/runtime/lib/router.test.js +58 -0
  5. package/dist/runtime/render/__rwsdk_ssr_bridge.d.ts +10 -0
  6. package/dist/runtime/render/__rwsdk_ssr_bridge.js +9 -0
  7. package/dist/runtime/render/__rwsdkssr_render.d.ts +9 -0
  8. package/dist/runtime/render/__rwsdkssr_render.js +13 -0
  9. package/dist/runtime/render/injectRSCPayload.js +8 -6
  10. package/dist/runtime/render/ssrBridge.d.ts +2 -0
  11. package/dist/runtime/render/ssrBridge.js +2 -0
  12. package/dist/runtime/render/ssrRenderToReadableStream.d.ts +2 -0
  13. package/dist/runtime/render/ssrRenderToReadableStream.js +2 -0
  14. package/dist/runtime/requestInfo/__rwsdknossr_worker.d.ts +5 -0
  15. package/dist/runtime/requestInfo/__rwsdknossr_worker.js +33 -0
  16. package/dist/runtime/worker.js +7 -3
  17. package/dist/vite/aliasedModuleResolver.d.mts +9 -0
  18. package/dist/vite/aliasedModuleResolver.mjs +62 -0
  19. package/dist/vite/aliasedSSRResolver.d.mts +5 -0
  20. package/dist/vite/aliasedSSRResolver.mjs +74 -0
  21. package/dist/vite/ensureConfigArrays.d.mts +1 -0
  22. package/dist/vite/ensureConfigArrays.mjs +12 -0
  23. package/dist/vite/findImportSpecifiers.d.mts +14 -0
  24. package/dist/vite/findImportSpecifiers.mjs +76 -0
  25. package/dist/vite/isBareImport.d.mts +1 -0
  26. package/dist/vite/isBareImport.mjs +5 -0
  27. package/dist/vite/moduleResolver.d.mts +10 -0
  28. package/dist/vite/moduleResolver.mjs +74 -0
  29. package/dist/vite/transformClientComponents.d.mts +11 -0
  30. package/dist/vite/transformClientComponents.mjs +171 -0
  31. package/dist/vite/transformServerReferences.d.mts +11 -0
  32. package/dist/vite/transformServerReferences.mjs +74 -0
  33. package/dist/vite/useClientPlugin.mjs +7 -3
  34. package/dist/vite/useClientPlugin.test.mjs +91 -1
  35. package/dist/vite/useServerPlugin.d.mts +3 -0
  36. package/dist/vite/useServerPlugin.mjs +134 -37
  37. package/dist/vite/useServerPlugin.test.d.mts +1 -0
  38. package/dist/vite/useServerPlugin.test.mjs +99 -0
  39. package/dist/vite/virtualizedSSRPlugin.d.mts +56 -0
  40. package/dist/vite/virtualizedSSRPlugin.mjs +464 -0
  41. package/package.json +1 -2
@@ -6,6 +6,7 @@ export type DocumentProps = RequestInfo & {
6
6
  export type RwContext = {
7
7
  nonce: string;
8
8
  Document: React.FC<DocumentProps>;
9
+ rscPayload: boolean;
9
10
  };
10
11
  export type RouteMiddleware = (requestInfo: RequestInfo) => Response | Promise<Response> | void | Promise<void> | Promise<Response | void>;
11
12
  type RouteFunction = (requestInfo: RequestInfo) => Response | Promise<Response>;
@@ -17,6 +18,7 @@ export type RouteDefinition = {
17
18
  path: string;
18
19
  handler: RouteHandler;
19
20
  };
21
+ export declare function matchPath(routePath: string, requestPath: string): RequestInfo["params"] | null;
20
22
  export declare function defineRoutes(routes: Route[]): {
21
23
  routes: Route[];
22
24
  handle: ({ request, renderPage, getRequestInfo, onError, runWithRequestInfoOverrides, }: {
@@ -30,5 +32,12 @@ export declare function defineRoutes(routes: Route[]): {
30
32
  export declare function route(path: string, handler: RouteHandler): RouteDefinition;
31
33
  export declare function index(handler: RouteHandler): RouteDefinition;
32
34
  export declare function prefix(prefix: string, routes: ReturnType<typeof route>[]): RouteDefinition[];
33
- export declare function render(Document: React.FC<DocumentProps>, routes: Route[]): Route[];
35
+ export declare function render(Document: React.FC<DocumentProps>, routes: Route[],
36
+ /**
37
+ * @param options - Configuration options for rendering.
38
+ * @param options.rscPayload - Toggle the RSC payload that's appended to the Document. Disabling this will mean that interactivity no longer works.
39
+ */
40
+ options?: {
41
+ rscPayload: boolean;
42
+ }): Route[];
34
43
  export {};
@@ -1,5 +1,18 @@
1
1
  import { isValidElementType } from "react-is";
2
- function matchPath(routePath, requestPath) {
2
+ export function matchPath(routePath, requestPath) {
3
+ // Check for invalid pattern: multiple colons in a segment (e.g., /:param1:param2/)
4
+ if (routePath.includes(":")) {
5
+ const segments = routePath.split("/");
6
+ for (const segment of segments) {
7
+ if ((segment.match(/:/g) || []).length > 1) {
8
+ throw new Error(`Invalid route pattern: segment "${segment}" in "${routePath}" contains multiple colons.`);
9
+ }
10
+ }
11
+ }
12
+ // Check for invalid pattern: double wildcard (e.g., /**/)
13
+ if (routePath.indexOf("**") !== -1) {
14
+ throw new Error(`Invalid route pattern: "${routePath}" contains "**". Use "*" for a single wildcard segment.`);
15
+ }
3
16
  const pattern = routePath
4
17
  .replace(/:[a-zA-Z0-9]+/g, "([^/]+)") // Convert :param to capture group
5
18
  .replace(/\*/g, "(.*)"); // Convert * to wildcard capture group
@@ -8,18 +21,33 @@ function matchPath(routePath, requestPath) {
8
21
  if (!matches) {
9
22
  return null;
10
23
  }
11
- // Extract named parameters and wildcards
24
+ // Revised parameter extraction:
12
25
  const params = {};
13
- const paramNames = [...routePath.matchAll(/:[a-zA-Z0-9]+/g)].map((m) => m[0].slice(1));
14
- const wildcardCount = (routePath.match(/\*/g) || []).length;
15
- // Add named parameters
16
- paramNames.forEach((name, i) => {
17
- params[name] = matches[i + 1];
18
- });
19
- // Add wildcard parameters with numeric indices
20
- for (let i = 0; i < wildcardCount; i++) {
21
- const wildcardIndex = paramNames.length + i + 1;
22
- params[`$${i}`] = matches[wildcardIndex];
26
+ let currentMatchIndex = 1; // Regex matches are 1-indexed
27
+ // This regex finds either a named parameter token (e.g., ":id") or a wildcard star token ("*").
28
+ const tokenRegex = /:([a-zA-Z0-9_]+)|\*/g;
29
+ let matchToken;
30
+ let wildcardCounter = 0;
31
+ // Ensure regex starts from the beginning of the routePath for each call if it's stateful (it is with /g)
32
+ tokenRegex.lastIndex = 0;
33
+ while ((matchToken = tokenRegex.exec(routePath)) !== null) {
34
+ // Ensure we have a corresponding match from the regex execution
35
+ if (matches[currentMatchIndex] === undefined) {
36
+ // This case should ideally not be hit if routePath and pattern generation are correct
37
+ // and all parts of the regex matched.
38
+ // Consider logging a warning or throwing an error if critical.
39
+ break;
40
+ }
41
+ if (matchToken[1]) {
42
+ // This token is a named parameter (e.g., matchToken[1] is "id" for ":id")
43
+ params[matchToken[1]] = matches[currentMatchIndex];
44
+ }
45
+ else {
46
+ // This token is a wildcard "*"
47
+ params[`$${wildcardCounter}`] = matches[currentMatchIndex];
48
+ wildcardCounter++;
49
+ }
50
+ currentMatchIndex++;
23
51
  }
24
52
  return params;
25
53
  }
@@ -104,9 +132,15 @@ export function prefix(prefix, routes) {
104
132
  };
105
133
  });
106
134
  }
107
- export function render(Document, routes) {
135
+ export function render(Document, routes,
136
+ /**
137
+ * @param options - Configuration options for rendering.
138
+ * @param options.rscPayload - Toggle the RSC payload that's appended to the Document. Disabling this will mean that interactivity no longer works.
139
+ */
140
+ options = { rscPayload: true }) {
108
141
  const documentMiddleware = ({ rw }) => {
109
142
  rw.Document = Document;
143
+ rw.rscPayload = options.rscPayload;
110
144
  };
111
145
  return [documentMiddleware, ...routes];
112
146
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,58 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { matchPath } from "./router";
3
+ describe("matchPath", () => {
4
+ // Test case 1: Static paths
5
+ it("should match static paths", () => {
6
+ expect(matchPath("/about/", "/about/")).toEqual({});
7
+ expect(matchPath("/contact/", "/contact/")).toEqual({});
8
+ });
9
+ it("should not match different static paths", () => {
10
+ expect(matchPath("/about/", "/service/")).toBeNull();
11
+ });
12
+ // Test case 2: Paths with parameters
13
+ it("should match paths with parameters and extract them", () => {
14
+ expect(matchPath("/users/:id/", "/users/123/")).toEqual({ id: "123" });
15
+ expect(matchPath("/posts/:category/:slug/", "/posts/tech/my-first-post/")).toEqual({ category: "tech", slug: "my-first-post" });
16
+ });
17
+ it("should not match if parameter is missing", () => {
18
+ expect(matchPath("/users/:id/", "/users/")).toBeNull();
19
+ });
20
+ // Test case 3: Paths with wildcards
21
+ it("should match paths with wildcards and extract them", () => {
22
+ expect(matchPath("/files/*/", "/files/document.pdf/")).toEqual({
23
+ $0: "document.pdf",
24
+ });
25
+ expect(matchPath("/data/*\/content/", "/data/archive/content/")).toEqual({
26
+ $0: "archive",
27
+ });
28
+ expect(matchPath("/assets/*/*/", "/assets/images/pic.png/")).toEqual({
29
+ $0: "images",
30
+ $1: "pic.png",
31
+ });
32
+ });
33
+ it("should match empty wildcard", () => {
34
+ expect(matchPath("/files/*/", "/files//")).toEqual({ $0: "" });
35
+ });
36
+ // Test case 4: Paths with both parameters and wildcards
37
+ it("should match paths with both parameters and wildcards", () => {
38
+ expect(matchPath("/products/:productId/*/", "/products/abc/details/more/")).toEqual({ productId: "abc", $0: "details/more" });
39
+ });
40
+ // Test case 5: Paths that don't match
41
+ it("should return null for non-matching paths", () => {
42
+ expect(matchPath("/specific/path/", "/a/different/path/")).toBeNull();
43
+ });
44
+ // Test case 6: Edge cases
45
+ it("should handle trailing slashes correctly", () => {
46
+ // Current implementation in defineRoutes adds a trailing slash if missing,
47
+ // and route() function also enforces it. matchPath itself doesn't normalize.
48
+ expect(matchPath("/path/", "/path")).toBeNull(); // Path to match must end with /
49
+ expect(matchPath("/path/", "/path/")).toEqual({});
50
+ });
51
+ it("should handle paths with multiple parameters and wildcards interspersed", () => {
52
+ expect(matchPath("/type/:typeId/item/*/:itemId/*/", "/type/a/item/image/b/thumb/")).toEqual({ typeId: "a", $0: "image", itemId: "b", $1: "thumb" });
53
+ });
54
+ it("should not allow named parameters or wildcards in the same path", () => {
55
+ expect(() => matchPath("/type/:typeId:is:broken", "/type/a-thumb-drive")).toThrow();
56
+ expect(() => matchPath("/type/**", "/type/a-thumb-drive")).toThrow();
57
+ });
58
+ });
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ import ReactServerDom from "react-dom/server.edge";
3
+ import { DocumentProps } from "../lib/router";
4
+ import { RequestInfo } from "../requestInfo/types";
5
+ export declare const renderRscThenableToHtmlStream: ({ thenable, Document, requestInfo, nonce, }: {
6
+ thenable: any;
7
+ Document: React.FC<DocumentProps>;
8
+ requestInfo: RequestInfo;
9
+ nonce?: string;
10
+ }) => Promise<ReactServerDom.ReactDOMServerReadableStream>;
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import ReactServerDom from "react-dom/server.edge";
4
+ const { use } = React;
5
+ const { renderToReadableStream } = ReactServerDom;
6
+ export const renderRscThenableToHtmlStream = ({ thenable, Document, requestInfo, nonce, }) => {
7
+ const Component = () => (_jsx(Document, { ...requestInfo, children: use(thenable).node }));
8
+ return renderToReadableStream(_jsx(Component, {}), { nonce });
9
+ };
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ import ReactServerDom from "react-dom/server.edge";
3
+ import { type DocumentProps } from "../lib/router";
4
+ import { type RequestInfo } from "../requestInfo/types";
5
+ export declare const renderRscThenableToHtmlStream: ({ thenable, Document, requestInfo, }: {
6
+ thenable: any;
7
+ Document: React.FC<DocumentProps>;
8
+ requestInfo: RequestInfo;
9
+ }) => Promise<ReactServerDom.ReactDOMServerReadableStream>;
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import ReactServerDom from "react-dom/server.edge";
4
+ const { use } = React;
5
+ const { renderToReadableStream } = ReactServerDom;
6
+ export const renderRscThenableToHtmlStream = async ({ thenable, Document, requestInfo, }) => {
7
+ const Component = () => {
8
+ return (_jsx(Document, { ...requestInfo, children: use(thenable).node }));
9
+ };
10
+ return await renderToReadableStream(_jsx(Component, {}), {
11
+ nonce: requestInfo.rw.nonce,
12
+ });
13
+ };
@@ -3,7 +3,7 @@
3
3
  const encoder = new TextEncoder();
4
4
  const trailer = "</body></html>";
5
5
  export function injectRSCPayload(rscStream, { nonce }) {
6
- const decoder = new TextDecoder("utf-8");
6
+ let decoder = new TextDecoder();
7
7
  let resolveFlightDataPromise;
8
8
  let flightDataPromise = new Promise((resolve) => (resolveFlightDataPromise = resolve));
9
9
  let startedRSC = false;
@@ -13,9 +13,12 @@ export function injectRSCPayload(rscStream, { nonce }) {
13
13
  let buffered = [];
14
14
  let timeout = null;
15
15
  function flushBufferedChunks(controller) {
16
- for (const chunk of buffered) {
17
- const html = decoder.decode(chunk, { stream: true });
18
- controller.enqueue(encoder.encode(html));
16
+ for (let chunk of buffered) {
17
+ let buf = decoder.decode(chunk);
18
+ if (buf.endsWith(trailer)) {
19
+ buf = buf.slice(0, -trailer.length);
20
+ }
21
+ controller.enqueue(encoder.encode(buf));
19
22
  }
20
23
  buffered.length = 0;
21
24
  timeout = null;
@@ -42,8 +45,7 @@ export function injectRSCPayload(rscStream, { nonce }) {
42
45
  clearTimeout(timeout);
43
46
  flushBufferedChunks(controller);
44
47
  }
45
- controller.enqueue(encoder.encode(decoder.decode()));
46
- controller.enqueue(encoder.encode(trailer));
48
+ controller.enqueue(encoder.encode("</body></html>"));
47
49
  },
48
50
  });
49
51
  }
@@ -0,0 +1,2 @@
1
+ import { renderToReadableStream } from "react-dom/server.edge";
2
+ export declare const ssrRenderToReadableStream: typeof renderToReadableStream;
@@ -0,0 +1,2 @@
1
+ import { renderToReadableStream } from "react-dom/server.edge";
2
+ export const ssrRenderToReadableStream = renderToReadableStream;
@@ -0,0 +1,2 @@
1
+ import { renderToReadableStream } from "react-dom/server.edge";
2
+ export declare const ssrRenderToReadableStream: typeof renderToReadableStream;
@@ -0,0 +1,2 @@
1
+ import { renderToReadableStream } from "react-dom/server.edge";
2
+ export const ssrRenderToReadableStream = renderToReadableStream;
@@ -0,0 +1,5 @@
1
+ import { RequestInfo, DefaultAppContext } from "./types";
2
+ export declare const requestInfo: RequestInfo<DefaultAppContext>;
3
+ export declare function getRequestInfo(): RequestInfo;
4
+ export declare function runWithRequestInfo<Result>(context: Record<string, any>, fn: () => Result): Result;
5
+ export declare function runWithRequestInfoOverrides<Result>(overrides: Record<string, any>, fn: () => Result): Result;
@@ -0,0 +1,33 @@
1
+ import { AsyncLocalStorage } from "async_hooks";
2
+ const requestInfoStore = new AsyncLocalStorage();
3
+ const requestInfoBase = {};
4
+ const REQUEST_INFO_KEYS = ["request", "params", "ctx", "headers", "rw", "cf"];
5
+ REQUEST_INFO_KEYS.forEach((key) => {
6
+ Object.defineProperty(requestInfoBase, key, {
7
+ enumerable: true,
8
+ configurable: false,
9
+ get: function () {
10
+ const store = requestInfoStore.getStore();
11
+ return store ? store[key] : undefined;
12
+ },
13
+ });
14
+ });
15
+ export const requestInfo = Object.freeze(requestInfoBase);
16
+ export function getRequestInfo() {
17
+ const store = requestInfoStore.getStore();
18
+ if (!store) {
19
+ throw new Error("Request context not found");
20
+ }
21
+ return store;
22
+ }
23
+ export function runWithRequestInfo(context, fn) {
24
+ return requestInfoStore.run(context, fn);
25
+ }
26
+ export function runWithRequestInfoOverrides(overrides, fn) {
27
+ const requestInfo = requestInfoStore.getStore();
28
+ const newRequestInfo = {
29
+ ...requestInfo,
30
+ ...overrides,
31
+ };
32
+ return requestInfoStore.run(newRequestInfo, fn);
33
+ }
@@ -37,6 +37,7 @@ export const defineApp = (routes) => {
37
37
  const rw = {
38
38
  Document: DefaultDocument,
39
39
  nonce: generateNonce(),
40
+ rscPayload: true,
40
41
  };
41
42
  const outerRequestInfo = {
42
43
  request,
@@ -104,9 +105,12 @@ export const defineApp = (routes) => {
104
105
  Parent: ({ children }) => (_jsx(rw.Document, { ...requestInfo, children: children })),
105
106
  nonce: rw.nonce,
106
107
  });
107
- const html = htmlStream.pipeThrough(injectRSCPayload(rscPayloadStream2, {
108
- nonce: rw.nonce,
109
- }));
108
+ let html = htmlStream;
109
+ if (rw.rscPayload) {
110
+ html = htmlStream.pipeThrough(injectRSCPayload(rscPayloadStream2, {
111
+ nonce: rw.nonce,
112
+ }));
113
+ }
110
114
  return new Response(html, {
111
115
  headers: {
112
116
  "content-type": "text/html; charset=utf-8",
@@ -0,0 +1,9 @@
1
+ export declare function createAliasedModuleResolver({ getAliases, roots, conditionNames, name, }: {
2
+ getAliases?: () => Array<{
3
+ find: string | RegExp;
4
+ replacement: string;
5
+ }>;
6
+ roots: string[];
7
+ conditionNames?: string[];
8
+ name?: string;
9
+ }): (request: string, importer: string) => string | false;
@@ -0,0 +1,62 @@
1
+ import path from "path";
2
+ import enhancedResolve from "enhanced-resolve";
3
+ import debug from "debug";
4
+ function applyAlias(request, aliasEntries, name) {
5
+ if (!aliasEntries)
6
+ return request;
7
+ const logPrefix = `[${name}]`;
8
+ // Support both array and object forms
9
+ const entries = Array.isArray(aliasEntries)
10
+ ? aliasEntries
11
+ : Object.entries(aliasEntries).map(([find, replacement]) => ({
12
+ find,
13
+ replacement,
14
+ }));
15
+ for (const entry of entries) {
16
+ const { find, replacement } = entry;
17
+ if (typeof find === "string") {
18
+ if (request === find || request.startsWith(find + "/")) {
19
+ debug("rwsdk:vite:aliased-module-resolver")("%s [applyAlias] Matched string alias: '%s' -> '%s' for request '%s'", logPrefix, find, replacement, request);
20
+ return replacement + request.slice(find.length);
21
+ }
22
+ }
23
+ else if (find instanceof RegExp) {
24
+ if (find.test(request)) {
25
+ debug("rwsdk:vite:aliased-module-resolver")("%s [applyAlias] Matched RegExp alias: %O -> '%s' for request '%s'", logPrefix, find, replacement, request);
26
+ return request.replace(find, replacement);
27
+ }
28
+ }
29
+ }
30
+ return request;
31
+ }
32
+ export function createAliasedModuleResolver({ getAliases, roots, conditionNames = ["workerd", "edge", "import", "default"], name = "aliasedModuleResolver", }) {
33
+ const log = debug("rwsdk:vite:aliased-module-resolver");
34
+ const logPrefix = `[${name}]`;
35
+ // Create a resolver instance with the provided conditionNames
36
+ const baseModuleResolver = enhancedResolve.create.sync({
37
+ conditionNames,
38
+ });
39
+ return function resolveModule(request, importer) {
40
+ log("%s Called with request: '%s', importer: '%s'", logPrefix, request, importer);
41
+ const aliasEntries = getAliases ? getAliases() : [];
42
+ log("%s Alias entries: %O", logPrefix, aliasEntries);
43
+ const normalized = applyAlias(request, aliasEntries, name);
44
+ log("%s After aliasing: '%s'", logPrefix, normalized);
45
+ const result = baseModuleResolver(normalized, path.dirname(importer));
46
+ if (result) {
47
+ log("%s Resolved %s relative to: '%s'", logPrefix, result, importer);
48
+ return result;
49
+ }
50
+ for (const root of roots) {
51
+ try {
52
+ const result = baseModuleResolver(root, normalized);
53
+ log("%s Resolved %s to: '%s' with root '%s'", logPrefix, normalized, result, root);
54
+ return result;
55
+ }
56
+ catch (err) {
57
+ log("%s Resolution failed for '%s' from root '%s': %O", logPrefix, normalized, root, err);
58
+ }
59
+ }
60
+ return false;
61
+ };
62
+ }
@@ -0,0 +1,5 @@
1
+ export declare function createAliasedSSRResolver({ getResolveConfig, roots, name, }: {
2
+ getResolveConfig: () => any;
3
+ roots: string[];
4
+ name?: string;
5
+ }): (request: string, importer: string) => string | false;
@@ -0,0 +1,74 @@
1
+ import path from "path";
2
+ import enhancedResolve from "enhanced-resolve";
3
+ import debug from "debug";
4
+ const baseSSRResolver = enhancedResolve.create.sync({
5
+ conditionNames: ["workerd", "edge", "import", "default"],
6
+ });
7
+ function applyAlias(request, aliasEntries, name) {
8
+ if (!aliasEntries)
9
+ return request;
10
+ const logPrefix = `[${name}]`;
11
+ // Support both array and object forms
12
+ const entries = Array.isArray(aliasEntries)
13
+ ? aliasEntries
14
+ : Object.entries(aliasEntries).map(([find, replacement]) => ({
15
+ find,
16
+ replacement,
17
+ }));
18
+ for (const entry of entries) {
19
+ const { find, replacement } = entry;
20
+ if (typeof find === "string") {
21
+ if (request === find || request.startsWith(find + "/")) {
22
+ debug("rwsdk:vite:aliased-ssr-resolver")("%s [applyAlias] Matched string alias: '%s' -> '%s' for request '%s'", logPrefix, find, replacement, request);
23
+ return replacement + request.slice(find.length);
24
+ }
25
+ }
26
+ else if (find instanceof RegExp) {
27
+ if (find.test(request)) {
28
+ debug("rwsdk:vite:aliased-ssr-resolver")("%s [applyAlias] Matched RegExp alias: %O -> '%s' for request '%s'", logPrefix, find, replacement, request);
29
+ return request.replace(find, replacement);
30
+ }
31
+ }
32
+ }
33
+ return request;
34
+ }
35
+ export function createAliasedSSRResolver({ getResolveConfig, roots, name = "aliasedSSRResolver", }) {
36
+ const log = debug("rwsdk:vite:aliased-ssr-resolver");
37
+ const logPrefix = `[${name}]`;
38
+ return function resolveModule(request, importer) {
39
+ log("%s Called with request: '%s', importer: '%s'", logPrefix, request, importer);
40
+ let normalized = request;
41
+ const resolveConfig = getResolveConfig?.() || {};
42
+ const aliasEntries = resolveConfig.alias;
43
+ log("%s Alias entries: %O", logPrefix, aliasEntries);
44
+ normalized = applyAlias(normalized, aliasEntries, name);
45
+ log("%s After aliasing: '%s'", logPrefix, normalized);
46
+ let rootsToTry = roots && roots.length > 0 ? roots : [];
47
+ // If leading slash, treat as first root-rooted (for compatibility)
48
+ if (normalized.startsWith("/")) {
49
+ if (rootsToTry.length > 0) {
50
+ const rooted = path.join(rootsToTry[0], normalized);
51
+ log("%s Leading slash detected, resolving as root[0]-rooted: '%s'", logPrefix, rooted);
52
+ normalized = rooted;
53
+ rootsToTry = [rootsToTry[0]];
54
+ }
55
+ }
56
+ const isAbsolute = path.isAbsolute(normalized);
57
+ if (isAbsolute) {
58
+ log("%s Resolving absolute path: '%s'", logPrefix, normalized);
59
+ return normalized;
60
+ }
61
+ for (const root of rootsToTry) {
62
+ try {
63
+ log("%s Trying root: '%s'", logPrefix, root);
64
+ const result = baseSSRResolver(root, normalized);
65
+ log("%s Resolved to: '%s' with root '%s'", logPrefix, result, root);
66
+ return result;
67
+ }
68
+ catch (err) {
69
+ log("%s Resolution failed for '%s' from root '%s': %O", logPrefix, normalized, root, err);
70
+ }
71
+ }
72
+ return false;
73
+ };
74
+ }
@@ -0,0 +1 @@
1
+ export declare const ensureConfigArrays: (config: any) => void;
@@ -0,0 +1,12 @@
1
+ export const ensureConfigArrays = (config) => {
2
+ config.optimizeDeps ??= {};
3
+ config.optimizeDeps.include ??= [];
4
+ config.optimizeDeps.esbuildOptions ??= {};
5
+ config.optimizeDeps.esbuildOptions.plugins ??= [];
6
+ config.resolve ??= {};
7
+ config.resolve.alias ??= [];
8
+ if (!Array.isArray(config.resolve.alias)) {
9
+ const aliasObj = config.resolve.alias;
10
+ config.resolve.alias = Object.entries(aliasObj).map(([find, replacement]) => ({ find, replacement }));
11
+ }
12
+ };
@@ -0,0 +1,14 @@
1
+ export declare const IMPORT_PATTERNS: string[];
2
+ /**
3
+ * Finds import specifiers and their positions in the code using the provided patterns.
4
+ * @param code The code to search for import specifiers.
5
+ * @param lang The language parser to use (TypeScript or Tsx).
6
+ * @param ignoredImportPatterns Array of regex patterns to ignore.
7
+ * @param log Optional logger function for debug output.
8
+ * @returns Array of objects with start, end, and raw import string.
9
+ */
10
+ export declare function findImportSpecifiers(id: string, code: string, ignoredImportPatterns: RegExp[], log?: (...args: any[]) => void): Array<{
11
+ s: number;
12
+ e: number;
13
+ raw: string;
14
+ }>;
@@ -0,0 +1,76 @@
1
+ import { parse as sgParse, Lang as SgLang, Lang } from "@ast-grep/napi";
2
+ import path from "path";
3
+ // These patterns are used to match import statements in code for SSR transformations.
4
+ export const IMPORT_PATTERNS = [
5
+ 'import { $$$ } from "$MODULE"',
6
+ "import { $$$ } from '$MODULE'",
7
+ 'import $DEFAULT from "$MODULE"',
8
+ "import $DEFAULT from '$MODULE'",
9
+ 'import * as $NS from "$MODULE"',
10
+ "import * as $NS from '$MODULE'",
11
+ 'import "$MODULE"',
12
+ "import '$MODULE'",
13
+ // Static Re-exports
14
+ 'export { $$$ } from "$MODULE"',
15
+ "export { $$$ } from '$MODULE'",
16
+ 'export * from "$MODULE"',
17
+ "export * from '$MODULE'",
18
+ // Dynamic Imports
19
+ 'import("$MODULE")',
20
+ "import('$MODULE')",
21
+ "import(`$MODULE`)",
22
+ // CommonJS require
23
+ 'require("$MODULE")',
24
+ "require('$MODULE')",
25
+ "require(`$MODULE`)",
26
+ ];
27
+ /**
28
+ * Finds import specifiers and their positions in the code using the provided patterns.
29
+ * @param code The code to search for import specifiers.
30
+ * @param lang The language parser to use (TypeScript or Tsx).
31
+ * @param ignoredImportPatterns Array of regex patterns to ignore.
32
+ * @param log Optional logger function for debug output.
33
+ * @returns Array of objects with start, end, and raw import string.
34
+ */
35
+ export function findImportSpecifiers(id, code, ignoredImportPatterns, log) {
36
+ const ext = path.extname(id).toLowerCase();
37
+ const lang = ext === ".tsx" || ext === ".jsx" ? Lang.Tsx : SgLang.TypeScript;
38
+ const logger = log ?? (() => { });
39
+ const results = [];
40
+ try {
41
+ // sgParse and lang must be provided by the consumer
42
+ const root = sgParse(lang, code);
43
+ for (const pattern of IMPORT_PATTERNS) {
44
+ try {
45
+ const matches = root.root().findAll(pattern);
46
+ for (const match of matches) {
47
+ const moduleCapture = match.getMatch("MODULE");
48
+ if (moduleCapture) {
49
+ const importPath = moduleCapture.text();
50
+ if (importPath.startsWith("virtual:")) {
51
+ logger(":findImportSpecifiersWithPositions: Ignoring import because it starts with 'virtual:': importPath=%s", importPath);
52
+ }
53
+ else if (importPath.includes("__rwsdknossr")) {
54
+ logger(":findImportSpecifiersWithPositions: Ignoring import because it includes '__rwsdknossr': importPath=%s", importPath);
55
+ }
56
+ else if (ignoredImportPatterns.some((pattern) => pattern.test(importPath))) {
57
+ logger(":findImportSpecifiersWithPositions: Ignoring import because it matches IGNORED_IMPORT_PATTERNS: importPath=%s", importPath);
58
+ }
59
+ else {
60
+ const { start, end } = moduleCapture.range();
61
+ results.push({ s: start.index, e: end.index, raw: importPath });
62
+ logger(":findImportSpecifiersWithPositions: Including import specifier: importPath=%s, range=[%d, %d]", importPath, start.index, end.index);
63
+ }
64
+ }
65
+ }
66
+ }
67
+ catch (err) {
68
+ logger(":findImportSpecifiersWithPositions: Error processing pattern: %O", err);
69
+ }
70
+ }
71
+ }
72
+ catch (err) {
73
+ logger(":findImportSpecifiersWithPositions: Error parsing content: %O", err);
74
+ }
75
+ return results;
76
+ }
@@ -0,0 +1 @@
1
+ export declare function isBareImport(importPath: string): boolean;
@@ -0,0 +1,5 @@
1
+ export function isBareImport(importPath) {
2
+ return (!importPath.startsWith(".") &&
3
+ !importPath.startsWith("/") &&
4
+ !importPath.startsWith("virtual:"));
5
+ }
@@ -0,0 +1,10 @@
1
+ export type ModuleResolver = ReturnType<typeof createModuleResolver>;
2
+ export declare function createModuleResolver({ getAliases, roots, conditionNames, name, }: {
3
+ getAliases?: () => Array<{
4
+ find: string | RegExp;
5
+ replacement: string;
6
+ }>;
7
+ roots: string[];
8
+ conditionNames?: string[];
9
+ name: string;
10
+ }): (request: string, importer?: string) => string | false;