Skip to content

Commit

Permalink
feat: managed mode (#580)
Browse files Browse the repository at this point in the history
* feat: managed mode

* does it work without entries.tsx and main.tsx?

* Revert "does it work without entries.tsx and main.tsx?"

This reverts commit 757ddc7.
  • Loading branch information
dai-shi committed Mar 8, 2024
1 parent 2852bd1 commit 6ebb3d2
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 1 deletion.
9 changes: 8 additions & 1 deletion packages/waku/src/lib/builder/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import { rscEntriesPlugin } from '../plugins/vite-plugin-rsc-entries.js';
import { rscServePlugin } from '../plugins/vite-plugin-rsc-serve.js';
import { rscEnvPlugin } from '../plugins/vite-plugin-rsc-env.js';
import { rscPrivatePlugin } from '../plugins/vite-plugin-rsc-private.js';
import { rscManagedPlugin } from '../plugins/vite-plugin-rsc-managed.js';
import { emitVercelOutput } from './output-vercel.js';
import { emitNetlifyOutput } from './output-netlify.js';
import { emitCloudflareOutput } from './output-cloudflare.js';
Expand Down Expand Up @@ -103,7 +104,10 @@ const analyzeEntries = async (
}
}
await buildVite({
plugins: [rscAnalyzePlugin(clientFileSet, serverFileSet, fileHashMap)],
plugins: [
rscAnalyzePlugin(clientFileSet, serverFileSet, fileHashMap),
rscManagedPlugin(config),
],
ssr: {
target: 'webworker',
resolve: {
Expand Down Expand Up @@ -172,6 +176,7 @@ const buildServerBundle = async (
}),
rscEnvPlugin({ config }),
rscPrivatePlugin(config),
rscManagedPlugin(config),
rscEntriesPlugin({
entriesFile,
moduleMap: {
Expand Down Expand Up @@ -274,6 +279,7 @@ const buildSsrBundle = async (
rscIndexPlugin({ ...config, cssAssets }),
rscEnvPlugin({ config }),
rscPrivatePlugin(config),
rscManagedPlugin(config),
],
ssr: isNodeCompatible
? {
Expand Down Expand Up @@ -338,6 +344,7 @@ const buildClientBundle = async (
rscIndexPlugin({ ...config, cssAssets }),
rscEnvPlugin({ config }),
rscPrivatePlugin(config),
rscManagedPlugin(config),
],
build: {
outDir: joinPath(rootDir, config.distDir, config.publicDir),
Expand Down
2 changes: 2 additions & 0 deletions packages/waku/src/lib/middleware/dev-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { rscIndexPlugin } from '../plugins/vite-plugin-rsc-index.js';
import { rscHmrPlugin, hotUpdate } from '../plugins/vite-plugin-rsc-hmr.js';
import { rscEnvPlugin } from '../plugins/vite-plugin-rsc-env.js';
import { rscPrivatePlugin } from '../plugins/vite-plugin-rsc-private.js';
import { rscManagedPlugin } from '../plugins/vite-plugin-rsc-managed.js';
import { mergeUserViteConfig } from '../utils/merge-vite-config.js';
import type { Middleware } from './types.js';

Expand Down Expand Up @@ -78,6 +79,7 @@ export const devServer: Middleware = (options) => {
patchReactRefresh(viteReact()),
rscEnvPlugin({ config }),
rscPrivatePlugin(config),
rscManagedPlugin(config),
rscIndexPlugin(config),
rscHmrPlugin(),
{ name: 'nonjs-resolve-plugin' }, // dummy to match with dev-worker-impl.ts
Expand Down
105 changes: 105 additions & 0 deletions packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import type { Plugin } from 'vite';

import { joinPath } from '../utils/path.js';

const getManagedMain = () => `
import { Component, StrictMode } from 'react';
import { createRoot, hydrateRoot } from 'react-dom/client';
import { Router } from 'waku/router/client';
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = {};
}
static getDerivedStateFromError(error) {
return { error };
}
render() {
if ('error' in this.state) {
return this.props.fallback(this.state.error);
}
return this.props.children;
}
}
const rootElement = (
<StrictMode>
<ErrorBoundary fallback={(error) => <h1>{String(error)}</h1>}>
<Router />
</ErrorBoundary>
</StrictMode>
);
if (document.body.dataset.hydrate) {
hydrateRoot(document.body, rootElement);
} else {
createRoot(document.body).render(rootElement);
}
`;

const getManagedEntries = () => `
import { fsRouter } from 'waku/router/server';
export default fsRouter(import.meta.url, loader);
function loader(dir, file) {
const p = file.replace(/\\.\\w+$/, '').split('/');
switch (p.length) {
${[...new Array(50).keys()]
.map(
(i) =>
' case ' +
(i + 1) +
': ' +
'return import(`./${dir}/' +
[...new Array(i + 1).keys()].map((j) => '${p[' + j + ']}').join('/') +
'.tsx`);',
)
.join('\n')}
default: throw new Error('too deep');
}
}
`;

export function rscManagedPlugin(opts: {
srcDir: string;
entriesJs: string;
mainJs?: string;
}): Plugin {
let entriesFile: string | undefined;
let mainFile: string | undefined;
const mainJsPath = opts.mainJs && '/' + joinPath(opts.srcDir, opts.mainJs);
let managedEntries = false;
let managedMain = false;
return {
name: 'rsc-managed-plugin',
enforce: 'pre',
configResolved(config) {
entriesFile = joinPath(config.root, opts.srcDir, opts.entriesJs);
if (opts.mainJs) {
mainFile = joinPath(config.root, opts.srcDir, opts.mainJs);
}
},
async resolveId(id, importer, options) {
const resolved = await this.resolve(id, importer, options);
if (!resolved && id === entriesFile) {
managedEntries = true;
return id;
}
if (!resolved && (id === mainFile || id === mainJsPath)) {
managedMain = true;
return id;
}
return resolved;
},
load(id) {
if (managedEntries && id === entriesFile) {
return getManagedEntries();
}
if (managedMain && (id === mainFile || id === mainJsPath)) {
return getManagedMain();
}
},
};
}
2 changes: 2 additions & 0 deletions packages/waku/src/lib/renderers/dev-worker-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { nonjsResolvePlugin } from '../plugins/vite-plugin-nonjs-resolve.js';
import { rscTransformPlugin } from '../plugins/vite-plugin-rsc-transform.js';
import { rscEnvPlugin } from '../plugins/vite-plugin-rsc-env.js';
import { rscPrivatePlugin } from '../plugins/vite-plugin-rsc-private.js';
import { rscManagedPlugin } from '../plugins/vite-plugin-rsc-managed.js';
import { rscDelegatePlugin } from '../plugins/vite-plugin-rsc-delegate.js';
import { mergeUserViteConfig } from '../utils/merge-vite-config.js';
import { viteHot } from '../plugins/vite-plugin-rsc-hmr.js';
Expand Down Expand Up @@ -132,6 +133,7 @@ const mergedViteConfig = await mergeUserViteConfig({
viteReact(),
rscEnvPlugin({}),
rscPrivatePlugin({ privateDir: configPrivateDir }),
rscManagedPlugin({ srcDir: configSrcDir, entriesJs: configEntriesJs }),
{ name: 'rsc-index-plugin' }, // dummy to match with handler-dev.ts
{ name: 'rsc-hmr-plugin', enforce: 'post' }, // dummy to match with handler-dev.ts
nonjsResolvePlugin(),
Expand Down

0 comments on commit 6ebb3d2

Please sign in to comment.