Skip to content

Commit

Permalink
breaking: server to allow fallback to static (#300)
Browse files Browse the repository at this point in the history
* wip: skipRenderRsc

* wip: ssr

* wip: router

* wip: should skip

* wip: isPrd not isBuild for getSsrConfig

* wip: fix some issues

* wip: shouldSkip

* use meta

* ex 10 and website

* fix a fixture

* wip: vercel output

* wip: test vercel output

* wip: test vercel output 2

* wip: test vercel output 3

* wip: test vercel output 4

* remove console.log

* refactor and some adjustments
  • Loading branch information
dai-shi committed Dec 23, 2023
1 parent c30a519 commit 8a1dd18
Show file tree
Hide file tree
Showing 32 changed files with 295 additions and 204 deletions.
2 changes: 1 addition & 1 deletion e2e/fixtures/rsc-basic/src/entries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default defineEntries(
};
},
// getBuildConfig
async () => [{ pathname: '/', entries: [['']] }],
async () => [{ pathname: '/', entries: [{ input: '' }] }],
// getSsrConfig
async () => {
throw new Error('SSR is should not be used in this test.');
Expand Down
10 changes: 6 additions & 4 deletions e2e/fixtures/rsc-router/src/entries.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { defineRouter } from 'waku/router/server';

const STATIC_PATHS = ['/', '/foo'];

export default defineRouter(
// getRoutePaths
async () => ({
static: ['/', '/foo'],
}),
// existsPath
async (path: string) => (STATIC_PATHS.includes(path) ? 'static' : null),
// getComponent (id is "**/layout" or "**/page")
async (id) => {
switch (id) {
Expand All @@ -18,4 +18,6 @@ export default defineRouter(
return null;
}
},
// getPathsForBuild
async () => STATIC_PATHS.map((path) => ({ path })),
);
2 changes: 1 addition & 1 deletion e2e/fixtures/ssr-basic/src/entries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default defineEntries(
};
},
// getBuildConfig
async () => [{ pathname: '/', entries: [['']] }],
async () => [{ pathname: '/', entries: [{ input: '' }] }],
// getSsrConfig
async ({ pathname }) => {
switch (pathname) {
Expand Down
1 change: 1 addition & 0 deletions examples/01_counter/src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const App = ({ name }: { name: string }) => {
<h1>Hello {name}!!</h1>
<h3>This is a server component.</h3>
<Counter />
<div>{new Date().toISOString()}</div>
</div>
);
};
Expand Down
2 changes: 1 addition & 1 deletion examples/01_counter/src/entries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default defineEntries(
};
},
// getBuildConfig
async () => [{ pathname: '/', entries: [['']] }],
async () => [{ pathname: '/', entries: [{ input: '' }] }],
// getSsrConfig
async ({ pathname }) => {
switch (pathname) {
Expand Down
1 change: 1 addition & 0 deletions examples/02_async/src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const App = ({ name }: { name: string }) => {
<ServerMessage />
</Suspense>
<Counter />
<div>{new Date().toISOString()}</div>
</div>
);
};
Expand Down
2 changes: 1 addition & 1 deletion examples/02_async/src/entries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default defineEntries(
};
},
// getBuildConfig
async () => [{ pathname: '/', entries: [['']] }],
async () => [{ pathname: '/', entries: [{ input: '' }] }],
// getSsrConfig
async ({ pathname }) => {
switch (pathname) {
Expand Down
2 changes: 1 addition & 1 deletion examples/03_promise/src/entries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default defineEntries(
};
},
// getBuildConfig
async () => [{ pathname: '/', entries: [['']] }],
async () => [{ pathname: '/', entries: [{ input: '' }] }],
// getSsrConfig
async ({ pathname }) => {
switch (pathname) {
Expand Down
1 change: 1 addition & 0 deletions examples/04_callserver/src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const App = ({ name }: { name: string }) => {
<h1>Hello {name}!!</h1>
<h3>This is a server component.</h3>
<Counter greet={greet as unknown as ServerFunction<typeof greet>} />
<div>{new Date().toISOString()}</div>
</div>
);
};
Expand Down
7 changes: 5 additions & 2 deletions examples/04_callserver/src/entries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@ export default defineEntries(
};
},
// getBuildConfig
async () => [{ pathname: '/', entries: [['']] }],
async () => [{ pathname: '/', entries: [{ input: '', isStatic: true }] }],
// getSsrConfig
async ({ pathname }) => {
async ({ pathname }, isPrd) => {
if (isPrd) {
return null;
}
switch (pathname) {
case '/':
return {
Expand Down
2 changes: 1 addition & 1 deletion examples/05_mutation/src/entries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default defineEntries(
};
},
// getBuildConfig
async () => [{ pathname: '/', entries: [['']] }],
async () => [{ pathname: '/', entries: [{ input: '' }] }],
// getSsrConfig
async ({ pathname }) => {
switch (pathname) {
Expand Down
12 changes: 6 additions & 6 deletions examples/06_nesting/src/entries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ export default defineEntries(
{
pathname: '/',
entries: [
[''],
['InnerApp=1', true],
['InnerApp=2', true],
['InnerApp=3', true],
['InnerApp=4', true],
['InnerApp=5', true],
{ input: '' },
{ input: 'InnerApp=1', skipPrefetch: true },
{ input: 'InnerApp=2', skipPrefetch: true },
{ input: 'InnerApp=3', skipPrefetch: true },
{ input: 'InnerApp=4', skipPrefetch: true },
{ input: 'InnerApp=5', skipPrefetch: true },
],
},
],
Expand Down
13 changes: 8 additions & 5 deletions examples/07_router/src/entries.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { defineRouter } from 'waku/router/server';

const STATIC_PATHS = ['/', '/foo', '/bar', '/nested/baz', '/nested/qux'];

export default defineRouter(
// getRoutePaths
async () => ({
static: ['/', '/foo', '/bar', '/nested/baz', '/nested/qux'],
}),
// existsPath
async (path: string) => (STATIC_PATHS.includes(path) ? 'static' : null),
// getComponent (id is "**/layout" or "**/page")
async (id) => {
async (id, unstable_setShouldSkip) => {
unstable_setShouldSkip({}); // always skip if possible
switch (id) {
case 'layout':
return import('./routes/layout.js');
Expand All @@ -26,4 +27,6 @@ export default defineRouter(
return null;
}
},
// getPathsForBuild
async () => STATIC_PATHS.map((path) => ({ path })),
);
2 changes: 1 addition & 1 deletion examples/07_router/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ErrorBoundary } from './components/ErrorBoundary.js';
const rootElement = (
<StrictMode>
<ErrorBoundary fallback={(error) => <h1>{String(error)}</h1>}>
<Router shouldSkip={() => true} />
<Router />
</ErrorBoundary>
</StrictMode>
);
Expand Down
4 changes: 3 additions & 1 deletion examples/08_cookies/src/entries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ export default defineEntries(
};
},
// getBuildConfig
async () => [{ pathname: '/', entries: [['']], context: { count: 0 } }],
async () => [
{ pathname: '/', entries: [{ input: '' }], context: { count: 0 } },
],
// getSsrConfig
async ({ pathname }) => {
switch (pathname) {
Expand Down
2 changes: 1 addition & 1 deletion examples/09_cssmodules/src/entries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default defineEntries(
};
},
// getBuildConfig
async () => [{ pathname: '/', entries: [['']] }],
async () => [{ pathname: '/', entries: [{ input: '' }] }],
// getSsrConfig
async ({ pathname }) => {
switch (pathname) {
Expand Down
35 changes: 20 additions & 15 deletions examples/10_dynamicroute/src/entries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,24 +54,27 @@ const getMappingAndItems = async (id: string) => {
return { mapping, items };
};

const getStaticPaths = async () => {
const files = await glob('**/page.{tsx,js}', { cwd: routesDir });
return files
.filter((file) => !/(^|\/)(\[\w+\]|_\w+_)\//.test(file))
.map((file) => '/' + file.slice(0, Math.max(0, file.lastIndexOf('/'))));
};

export default defineRouter(
// getRoutePaths
async () => {
const files = await glob('**/page.{tsx,js}', { cwd: routesDir });
const staticRoutes = files
.filter((file) => !/(^|\/)(\[\w+\]|_\w+_)\//.test(file))
.map((file) => '/' + file.slice(0, Math.max(0, file.lastIndexOf('/'))));
const dynamicRoutes = async (path: string) => {
const result = await getMappingAndItems(path + '/page');
return result !== null;
};
return {
static: staticRoutes,
dynamic: dynamicRoutes,
};
// existsPath
async (path: string) => {
if ((await getStaticPaths()).includes(path)) {
return 'static';
}
if ((await getMappingAndItems(path + '/page')) !== null) {
return 'dynamic';
}
return null;
},
// getComponent (id is "**/layout" or "**/page")
async (id) => {
async (id, unstable_setShouldSkip) => {
unstable_setShouldSkip({}); // always skip if possible
const result = await getMappingAndItems(id);
if (result === null) {
return null;
Expand All @@ -83,4 +86,6 @@ export default defineRouter(
);
return Component;
},
// getPathsForBuild
async () => (await getStaticPaths()).map((path) => ({ path })),
);
2 changes: 1 addition & 1 deletion examples/10_dynamicroute/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ErrorBoundary } from './components/ErrorBoundary.js';
const rootElement = (
<StrictMode>
<ErrorBoundary fallback={(error) => <h1>{String(error)}</h1>}>
<Router shouldSkip={() => true} />
<Router />
</ErrorBoundary>
</StrictMode>
);
Expand Down
2 changes: 1 addition & 1 deletion examples/11_form/src/entries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default defineEntries(
};
},
// getBuildConfig
async () => [{ pathname: '/', entries: [['']] }],
async () => [{ pathname: '/', entries: [{ input: '' }] }],
// getSsrConfig
async ({ pathname }) => {
switch (pathname) {
Expand Down
2 changes: 1 addition & 1 deletion examples/12_css/src/entries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default defineEntries(
};
},
// getBuildConfig
async () => [{ pathname: '/', entries: [['']] }],
async () => [{ pathname: '/', entries: [{ input: '' }] }],
// getSsrConfig
async ({ pathname }) => {
switch (pathname) {
Expand Down
16 changes: 14 additions & 2 deletions packages/waku/src/lib/builder/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,9 +314,13 @@ const emitRscFiles = async (
return Array.from(idSet || []);
};
const rscFileSet = new Set<string>(); // XXX could be implemented better
const staticInputSet = new Set<string>();
await Promise.all(
Array.from(buildConfig).map(async ({ entries, context }) => {
for (const [input] of entries || []) {
for (const { input, isStatic } of entries || []) {
if (isStatic) {
staticInputSet.add(input);
}
const destRscFile = joinPath(
rootDir,
config.distDir,
Expand Down Expand Up @@ -347,6 +351,13 @@ const emitRscFiles = async (
}
}),
);
const skipRenderRscCode = `
const staticInputSet = new Set(${JSON.stringify(Array.from(staticInputSet))});
export function skipRenderRsc(input) {
return staticInputSet.has(input);
}
`;
await appendFile(distEntriesFile, skipRenderRscCode);
return { buildConfig, getClientModules, rscFiles: Array.from(rscFileSet) };
};

Expand Down Expand Up @@ -388,7 +399,7 @@ const emitHtmlFiles = async (
);
const inputsForPrefetch = new Set<string>();
const moduleIdsForPrefetch = new Set<string>();
for (const [input, skipPrefetch] of entries || []) {
for (const { input, skipPrefetch } of entries || []) {
if (!skipPrefetch) {
inputsForPrefetch.add(input);
for (const id of getClientModules(input)) {
Expand Down Expand Up @@ -428,6 +439,7 @@ const emitHtmlFiles = async (
}),
isDev: false,
entries: distEntries,
isBuild: true,
}));
await mkdir(joinPath(destHtmlFile, '..'), { recursive: true });
if (htmlReadable) {
Expand Down
18 changes: 17 additions & 1 deletion packages/waku/src/lib/builder/output-vercel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,26 @@ export const emitVercelOutput = async (
path.join(serverlessDir, 'serve.js'),
`
import path from 'node:path';
import fs from 'node:fs';
import { connectMiddleware } from 'waku';
const entries = import(path.resolve('${config.distDir}', '${config.entriesJs}'));
export default async function handler(req, res) {
export default function handler(req, res) {
connectMiddleware({ entries, ssr: ${ssr} })(req, res, () => {
const fname = path.join(
'${config.distDir}',
'${config.publicDir}',
req.url,
path.extname(req.url) ? '' : '${config.indexHtml}',
);
if (fs.existsSync(fname)) {
if (fname.endsWith('.html')) {
res.setHeader('content-type', 'text/html; charset=utf-8');
} else if (fname.endsWith('.txt')) {
res.setHeader('content-type', 'text/plain');
}
fs.createReadStream(fname).pipe(res);
return;
}
res.statusCode = 404;
res.end();
});
Expand Down
Loading

1 comment on commit 8a1dd18

@vercel
Copy link

@vercel vercel bot commented on 8a1dd18 Dec 23, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

waku – ./

waku-git-main-daishi.vercel.app
waku.vercel.app
waku-daishi.vercel.app
waku.gg
www.waku.gg

Please sign in to comment.