Skip to content

Commit

Permalink
renderer for bare React components (wip)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jake Donham committed Apr 12, 2024
1 parent eaaff61 commit c4ec178
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 4 deletions.
25 changes: 21 additions & 4 deletions packages/server/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,13 @@ export function removeTimestampQuery(url: string): string {
type SourceDescription = {
code: string;
ast: babelTypes.Program;
type: "server" | "client" | "react";
};

const cells = new Map<string, SourceDescription>();

const server = await createViteServer({
server: { host: "127.0.0.1" },
server: { host: "127.0.0.1", strictPort: true },
plugins: [
{
name: "vitale",
Expand Down Expand Up @@ -80,6 +81,8 @@ const runtime = new ViteRuntime(
);

function rewriteCode(code: string, language: string): SourceDescription {
let type: "server" | "client" | "react" = "server";

const plugins = ((): ParserOptions["plugins"] => {
switch (language) {
case "typescriptreact":
Expand Down Expand Up @@ -107,6 +110,9 @@ function rewriteCode(code: string, language: string): SourceDescription {
program = babelTypes.program([
babelTypes.exportDefaultDeclaration(exprAst),
]);
if (exprAst.type === "JSXElement") {
type = "react";
}
} else {
const ast = babelParser.parse(code, parserOptions);
if (ast.program.body.length === 0) {
Expand All @@ -117,16 +123,21 @@ function rewriteCode(code: string, language: string): SourceDescription {
program = ast.program;
const body = ast.program.body;
const last = body[body.length - 1];
if (last.type === "ExpressionStatement") {
if (program.directives[0]?.value.value === "use client") {
type = "client";
} else if (last.type === "ExpressionStatement") {
const defaultExport = babelTypes.exportDefaultDeclaration(
last.expression
);
body[body.length - 1] = defaultExport;
if (last.expression.type === "JSXElement") {
type = "react";
}
}
}
}
const generatorResult = new babelGenerator.CodeGenerator(program).generate();
return { ast: program, code: generatorResult.code };
return { ast: program, code: generatorResult.code, type };
}

interface PossibleSVG {
Expand Down Expand Up @@ -171,12 +182,18 @@ async function executeCell(id: string, path: string, cellId: string) {
let mime;

// client execution
if (cells.get(id)?.ast.directives[0]?.value.value === "use client") {
if (cells.get(id)?.type === "client") {
data = JSON.stringify({
// TODO(jaked) strip workspace root when executeCell is called
id: id.substring(server.config.root.length + 1),
});
mime = "application/x-vitale";
} else if (cells.get(id)?.type === "react") {
data = JSON.stringify({
// TODO(jaked) strip workspace root when executeCell is called
id: id.substring(server.config.root.length + 1),
});
mime = "application/x-vitale-react";
}

// server execution
Expand Down
8 changes: 8 additions & 0 deletions packages/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@
"mimeTypes": [
"application/x-vitale"
]
},
{
"id": "vitale-react-renderer",
"displayName": "Vitale React Renderer",
"entrypoint": "./dist/vitaleReactRenderer.mjs",
"mimeTypes": [
"application/x-vitale-react"
]
}
]
},
Expand Down
62 changes: 62 additions & 0 deletions packages/vscode/src/vitaleReactRenderer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/// <reference lib="dom" />
import type { ActivationFunction } from "vscode-notebook-renderer";

const cellIdRegex = /^([^?]+\.vnb)\?cellId=([a-zA-z0-9_-]{21})\.([a-z]+)$/;

export const activate: ActivationFunction = (_context) => ({
async renderOutputItem(outputItem, element) {
element.replaceChildren();

const { id } = outputItem.json();
const match = cellIdRegex.exec(id);
if (!match) {
element.textContent = "Error: invalid cellId";
return;
}

const [_, _path, cellId] = match;

const root = document.createElement("div");
root.id = `cell-output-root-${cellId}`;
element.appendChild(root);

const script = document.createElement("script");
script.type = "module";

// Nota bene:
// 1. script.appendChild(document.createTextNode(...)) not script.innerText = ...
// or else the browser parses the script weirdly (comments kill script!)
// 2. assign base.href to dev server because extension env has special base
// (but we should handle this differently, maybe rewrite relative links)
// 3. VS Code does some shenanigans with the container height,
// and if rendering happens at the wrong time the container gets 0 height
// so we wait before importing the cell code
// (there is probably a better way to fix this)
script.appendChild(
document.createTextNode(`
const __vite__cjsImport0_react_jsxDevRuntime = (await import("http://localhost:5173/node_modules/.vite/deps/react_jsx-dev-runtime.js?v=e04bfa81")).default;
const jsxDEV = __vite__cjsImport0_react_jsxDevRuntime["jsxDEV"];
const __vite__cjsImport1_react = (await import("http://localhost:5173/node_modules/.vite/deps/react.js?v=e04bfa81")).default;
const React = __vite__cjsImport1_react.__esModule ? __vite__cjsImport1_react.default : __vite__cjsImport1_react;
const __vite__cjsImport2_reactDom_client = (await import("http://localhost:5173/node_modules/.vite/deps/react-dom_client.js?v=e04bfa81")).default;
const ReactDOM = __vite__cjsImport2_reactDom_client.__esModule ? __vite__cjsImport2_reactDom_client.default : __vite__cjsImport2_reactDom_client;
import RefreshRuntime from "http://localhost:5173/@react-refresh";
RefreshRuntime.injectIntoGlobalHook(window);
window.$RefreshReg$ = () => {};
window.$RefreshSig$ = () => (type) => type;
window.__vite_plugin_react_preamble_installed__ = true;
document.getElementsByTagName("base")[0].href = "http://localhost:5173/";
await new Promise((resolve) => setTimeout(resolve, 50));
const Component = (await import("http://localhost:5173/${id}&t=${Date.now()}")).default;
const root = ReactDOM.createRoot(document.getElementById("${root.id}"));
root.render(jsxDEV(React.StrictMode, { children: Component }, void 0, false));
`)
);
element.appendChild(script);
},
});
11 changes: 11 additions & 0 deletions packages/vscode/tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,15 @@ export default defineConfig([
};
},
},
{
entry: ["./src/vitaleReactRenderer.tsx"],
external: ["vscode"],
format: ["esm"],
splitting: false,
esbuildOptions(options) {
options.define = {
"process.env.NODE_ENV": JSON.stringify("production"),
};
},
},
]);

0 comments on commit c4ec178

Please sign in to comment.