Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion packages/waku/src/lib/utils/managed.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { EXTENSIONS, SRC_MIDDLEWARE, SRC_PAGES } from '../constants.js';

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove changes in this file.

export const getManagedServerEntry = (srcDir: string) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if I 100% follow the idea, but it shouldn't be managed mode only. So, changing code here seems not ideal.

const globBase = `/${srcDir}/${SRC_PAGES}`;
const exts = EXTENSIONS.map((ext) => ext.slice(1)).join(',');
Expand Down
2 changes: 1 addition & 1 deletion packages/waku/src/lib/vite-entries/entry.build.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createRequire } from 'node:module';
import { pathToFileURL } from 'node:url';
import serverEntry from 'virtual:vite-rsc-waku/server-entry';
import serverEntry from 'virtual:vite-rsc-waku/server-entry-build';
import { INTERNAL_setAllEnv } from '../../server.js';
import type { Unstable_EmitFile } from '../types.js';
import { joinPath } from '../utils/path.js';
Expand Down
2 changes: 1 addition & 1 deletion packages/waku/src/lib/vite-entries/entry.server.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import serverEntry from 'virtual:vite-rsc-waku/server-entry';
import serverEntry from 'virtual:vite-rsc-waku/server-entry-runtime';
import { INTERNAL_setAllEnv } from '../../server.js';

export { serverEntry as unstable_serverEntry };
Expand Down
35 changes: 30 additions & 5 deletions packages/waku/src/lib/vite-plugins/user-entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,23 @@ export function userEntriesPlugin({ srcDir }: { srcDir: string }): Plugin {
// resolve user entries and fallbacks to "managed mode" if not found.
async resolveId(source, _importer, options) {
if (source === 'virtual:vite-rsc-waku/server-entry') {
return '\0virtual:vite-rsc-waku/server-entry-runtime';
}
if (source === 'virtual:vite-rsc-waku/server-entry-runtime') {
return '\0' + source;
}
if (source === 'virtual:vite-rsc-waku/server-entry-build') {
return '\0' + source;
}
if (source === 'virtual:vite-rsc-waku/server-entry-inner') {
if (source === 'virtual:vite-rsc-waku/server-entry-runtime-inner') {
const resolved = await this.resolve(
`/${srcDir}/${SRC_SERVER_ENTRY}`,
undefined,
options,
);
return resolved ? resolved : '\0' + source;
}
if (source === 'virtual:vite-rsc-waku/server-entry-build-inner') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any difference between -runtime-inner and -build-inner?

const resolved = await this.resolve(
`/${srcDir}/${SRC_SERVER_ENTRY}`,
undefined,
Expand All @@ -30,16 +44,27 @@ export function userEntriesPlugin({ srcDir }: { srcDir: string }): Plugin {
return resolved ? resolved : '\0' + source;
}
},
load(id) {
if (id === '\0virtual:vite-rsc-waku/server-entry') {
async load(id) {
if (id === '\0virtual:vite-rsc-waku/server-entry-runtime') {
return `\
export { default } from 'virtual:vite-rsc-waku/server-entry-inner';
export { default } from 'virtual:vite-rsc-waku/server-entry-runtime-inner';
if (import.meta.hot) {
import.meta.hot.accept()
}
`;
}
if (id === '\0virtual:vite-rsc-waku/server-entry-inner') {
if (id === '\0virtual:vite-rsc-waku/server-entry-build') {
return `\
export { default } from 'virtual:vite-rsc-waku/server-entry-build-inner';
if (import.meta.hot) {
import.meta.hot.accept()
}
`;
}
if (id === '\0virtual:vite-rsc-waku/server-entry-runtime-inner') {
return getManagedServerEntry(srcDir);
}
if (id === '\0virtual:vite-rsc-waku/server-entry-build-inner') {
return getManagedServerEntry(srcDir);
}
if (id === '\0virtual:vite-rsc-waku/client-entry') {
Expand Down
10 changes: 10 additions & 0 deletions packages/waku/src/lib/vite-types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ declare module 'virtual:vite-rsc-waku/server-entry' {
export default default_;
}

declare module 'virtual:vite-rsc-waku/server-entry-runtime' {
const default_: import('./types.ts').Unstable_ServerEntry;
export default default_;
}

declare module 'virtual:vite-rsc-waku/server-entry-build' {
const default_: import('./types.ts').Unstable_ServerEntry;
export default default_;
}

declare module 'virtual:vite-rsc-waku/client-entry' {}

declare module 'virtual:vite-rsc-waku/build-metadata' {
Expand Down
47 changes: 35 additions & 12 deletions packages/waku/src/router/define-router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -231,16 +231,29 @@ globalThis.__WAKU_ROUTER_PREFETCH__ = (path, callback) => {
export function unstable_defineRouter(fns: {
getConfigs: () => Promise<Iterable<RouteConfig | ApiConfig | SliceConfig>>;
}) {
type RouterMode = 'runtime' | 'build';

// This is an internal type for caching
type MyConfig = {
configs: (RouteConfig | ApiConfig | SliceConfig)[];
has404: boolean;
};

let cachedMyConfig: MyConfig | undefined;
const getMyConfig = async (): Promise<MyConfig> => {
const cachedMyConfigByMode = new Map<RouterMode, MyConfig>();
const getMyConfig = async (mode: RouterMode = 'runtime'): Promise<MyConfig> => {
let cachedMyConfig = cachedMyConfigByMode.get(mode);
if (!cachedMyConfig) {
const configs = Array.from(await fns.getConfigs());
const allConfigs = Array.from(await fns.getConfigs());
const configs =
mode === 'runtime'
? allConfigs.filter(
(item) =>
!(
(item.type === 'route' || item.type === 'api') &&
item.isStatic
),
)
: allConfigs;
let has404 = false;
configs.forEach((item) => {
if (item.type === 'route') {
Expand All @@ -251,12 +264,16 @@ export function unstable_defineRouter(fns: {
}
});
cachedMyConfig = { configs, has404 };
cachedMyConfigByMode.set(mode, cachedMyConfig);
}
return cachedMyConfig;
};

const getPathConfigItem = async (pathname: string) => {
const myConfig = await getMyConfig();
const getPathConfigItem = async (
pathname: string,
mode: RouterMode = 'runtime',
) => {
const myConfig = await getMyConfig(mode);
const found = myConfig.configs.find(
(item): item is typeof item & { type: 'route' | 'api' } =>
(item.type === 'route' || item.type === 'api') &&
Expand Down Expand Up @@ -292,11 +309,12 @@ export function unstable_defineRouter(fns: {
headers: Readonly<Record<string, string>>,
getCachedElement: (id: SlotId) => Promise<ReactNode> | undefined,
setCachedElement: (id: SlotId, element: ReactNode) => Promise<ReactNode>,
mode: RouterMode = 'runtime',
) => {
setRscPath(rscPath);
setRscParams(rscParams);
const pathname = decodeRoutePath(rscPath);
const pathConfigItem = await getPathConfigItem(pathname);
const pathConfigItem = await getPathConfigItem(pathname, mode);
if (pathConfigItem?.type !== 'route') {
return null;
}
Expand All @@ -313,7 +331,7 @@ export function unstable_defineRouter(fns: {
pathname,
query: pathConfigItem.isStatic ? undefined : query,
};
const myConfig = await getMyConfig();
const myConfig = await getMyConfig(mode);
const slices = pathConfigItem.slices || [];
const sliceConfigMap = new Map<
string,
Expand Down Expand Up @@ -450,7 +468,7 @@ export function unstable_defineRouter(fns: {
return cachedPath2moduleIds!;
};

const pathConfigItem = await getPathConfigItem(input.pathname);
const pathConfigItem = await getPathConfigItem(input.pathname, 'runtime');
if (pathConfigItem?.type === 'api') {
const url = new URL(input.req.url);
url.pathname = input.pathname;
Expand All @@ -466,7 +484,7 @@ export function unstable_defineRouter(fns: {
if (sliceId !== null) {
// LIMITATION: This is a signle slice request.
// Ideally, we should be able to respond with multiple slices in one request.
const sliceConfig = await getMyConfig().then((myConfig) =>
const sliceConfig = await getMyConfig('runtime').then((myConfig) =>
myConfig.configs.find(
(item): item is typeof item & { type: 'slice' } =>
item.type === 'slice' && item.id === sliceId,
Expand Down Expand Up @@ -496,6 +514,7 @@ export function unstable_defineRouter(fns: {
headers,
getCachedElement,
setCachedElement,
'runtime',
);
if (!entries) {
return null;
Expand All @@ -520,6 +539,7 @@ export function unstable_defineRouter(fns: {
headers,
getCachedElement,
setCachedElement,
'runtime',
),
]).then(([oldElements, newElements]) => {
if (newElements === null) {
Expand All @@ -545,6 +565,7 @@ export function unstable_defineRouter(fns: {
headers,
getCachedElement,
setCachedElement,
'runtime',
);
if (!entries) {
unstable_notFound();
Expand All @@ -571,6 +592,7 @@ export function unstable_defineRouter(fns: {
headers,
getCachedElement,
setCachedElement,
'runtime',
);
if (!entries) {
return null;
Expand Down Expand Up @@ -607,7 +629,7 @@ export function unstable_defineRouter(fns: {
throw e;
}
}
if ((await getMyConfig()).has404) {
if ((await getMyConfig('runtime')).has404) {
return renderIt('/404', '', 404);
} else {
return null;
Expand All @@ -625,7 +647,7 @@ export function unstable_defineRouter(fns: {
generateFile,
generateDefaultHtml,
}) => {
const myConfig = await getMyConfig();
const myConfig = await getMyConfig('build');
const cachedElementsForBuild = new Map<SlotId, Promise<ReactNode>>();
const serializedCachedElements = new Map<SlotId, string>();
const getCachedElement = (id: SlotId) => cachedElementsForBuild.get(id);
Expand Down Expand Up @@ -699,6 +721,7 @@ export function unstable_defineRouter(fns: {
{},
getCachedElement,
setCachedElement,
'build',
);
if (!entries) {
return;
Expand Down Expand Up @@ -795,6 +818,6 @@ export function unstable_defineRouter(fns: {
};

return Object.assign(defineHandlers({ handleRequest, handleBuild }), {
unstable_getRouterConfigs: () => getMyConfig().then((c) => c.configs),
unstable_getRouterConfigs: () => getMyConfig('build').then((c) => c.configs),
});
}
33 changes: 22 additions & 11 deletions pnpm-workspace.yaml
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not change this in this PR.

Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
packages:
- 'packages/*'
- 'examples/*'
- 'e2e/fixtures/*'
# package.json in dist is generated by `waku build`
- packages/*
- examples/*
- e2e/fixtures/*
- '!**/dist'
preferWorkspacePackages: true
saveWorkspaceProtocol: true

linkWorkspacePackages: true

minimumReleaseAge: 1440

minimumReleaseAgeExclude:
- '@vitejs/plugin-rsc'
- 'react'
- 'react-dom'
- 'react-server-dom-webpack'
- 'scheduler'
- 'waku-jotai'
- react
- react-dom
- react-server-dom-webpack
- scheduler
- waku-jotai

onlyBuiltDependencies:
- '@swc/core'
- esbuild
- sharp
- unrs-resolver
- workerd

preferWorkspacePackages: true

saveWorkspaceProtocol: true