Major Bugfix. Fix server component client compatibility
This commit is contained in:
+18
-4
@@ -1,4 +1,4 @@
|
||||
import { existsSync, statSync, writeFileSync } from "fs";
|
||||
import { readFileSync, writeFileSync } from "fs";
|
||||
import * as esbuild from "esbuild";
|
||||
import grabAllPages from "../../utils/grab-all-pages";
|
||||
import grabDirNames from "../../utils/grab-dir-names";
|
||||
@@ -8,7 +8,7 @@ import { log } from "../../utils/log";
|
||||
import tailwindEsbuildPlugin from "../server/web-pages/tailwind-esbuild-plugin";
|
||||
import grabClientHydrationScript from "./grab-client-hydration-script";
|
||||
import grabArtifactsFromBundledResults from "./grab-artifacts-from-bundled-result";
|
||||
import path from "path";
|
||||
import stripServerSideLogic from "./strip-server-side-logic";
|
||||
const { HYDRATION_DST_DIR, HYDRATION_DST_DIR_MAP_JSON_FILE, ROOT_DIR } = grabDirNames();
|
||||
let build_starts = 0;
|
||||
const MAX_BUILD_STARTS = 10;
|
||||
@@ -18,9 +18,11 @@ export default async function allPagesBundler(params) {
|
||||
const dev = isDevelopment();
|
||||
for (const page of pages) {
|
||||
const key = page.local_path;
|
||||
const txt = grabClientHydrationScript({
|
||||
const txt = await grabClientHydrationScript({
|
||||
page_local_path: page.local_path,
|
||||
});
|
||||
if (!txt)
|
||||
continue;
|
||||
virtualEntries[key] = txt;
|
||||
}
|
||||
const virtualPlugin = {
|
||||
@@ -35,6 +37,19 @@ export default async function allPagesBundler(params) {
|
||||
loader: "tsx",
|
||||
resolveDir: process.cwd(),
|
||||
}));
|
||||
build.onLoad({ filter: /\.tsx$/ }, (args) => {
|
||||
if (args.path.includes("node_modules"))
|
||||
return;
|
||||
const source = readFileSync(args.path, "utf8");
|
||||
if (!source.includes("server")) {
|
||||
return { contents: source, loader: "tsx" };
|
||||
}
|
||||
const strippedCode = stripServerSideLogic({ txt_code: source });
|
||||
return {
|
||||
contents: strippedCode,
|
||||
loader: "tsx",
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
||||
const artifactTracker = {
|
||||
@@ -47,7 +62,6 @@ export default async function allPagesBundler(params) {
|
||||
if (build_starts == MAX_BUILD_STARTS) {
|
||||
const error_msg = `Build Failed. Please check all your components and imports.`;
|
||||
log.error(error_msg);
|
||||
// process.exit(1);
|
||||
}
|
||||
});
|
||||
build.onEnd((result) => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
type Params = {
|
||||
page_local_path: string;
|
||||
};
|
||||
export default function grabClientHydrationScript({ page_local_path }: Params): string;
|
||||
export default function grabClientHydrationScript({ page_local_path, }: Params): Promise<string>;
|
||||
export {};
|
||||
|
||||
+4
-2
@@ -3,9 +3,11 @@ import path from "path";
|
||||
import grabDirNames from "../../utils/grab-dir-names";
|
||||
import AppNames from "../../utils/grab-app-names";
|
||||
import grabConstants from "../../utils/grab-constants";
|
||||
import pagePathTransform from "../../utils/page-path-transform";
|
||||
const { PAGES_DIR } = grabDirNames();
|
||||
export default function grabClientHydrationScript({ page_local_path }) {
|
||||
export default async function grabClientHydrationScript({ page_local_path, }) {
|
||||
const { ClientRootElementIDName, ClientRootComponentWindowName, ClientWindowPagePropsName, } = grabConstants();
|
||||
const target_path = pagePathTransform({ page_path: page_local_path });
|
||||
const root_component_path = path.join(PAGES_DIR, `${AppNames["RootPagesComponentName"]}.tsx`);
|
||||
const does_root_exist = existsSync(root_component_path);
|
||||
let txt = ``;
|
||||
@@ -13,7 +15,7 @@ export default function grabClientHydrationScript({ page_local_path }) {
|
||||
if (does_root_exist) {
|
||||
txt += `import Root from "${root_component_path}";\n`;
|
||||
}
|
||||
txt += `import Page from "${page_local_path}";\n\n`;
|
||||
txt += `import Page from "${target_path}";\n\n`;
|
||||
txt += `const pageProps = window.${ClientWindowPagePropsName} || {};\n`;
|
||||
if (does_root_exist) {
|
||||
txt += `const component = <Root suppressHydrationWarning={true} {...pageProps}><Page {...pageProps} /></Root>\n`;
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
type Params = {
|
||||
txt_code: string;
|
||||
};
|
||||
export default function stripServerSideLogic({ txt_code }: Params): string;
|
||||
export {};
|
||||
@@ -0,0 +1,61 @@
|
||||
import ts from "typescript";
|
||||
export default function stripServerSideLogic({ txt_code }) {
|
||||
const sourceFile = ts.createSourceFile("temp.tsx", txt_code, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
|
||||
const transformer = (context) => {
|
||||
return (rootNode) => {
|
||||
const visitor = (node) => {
|
||||
if (ts.isVariableStatement(node) &&
|
||||
node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)) {
|
||||
const isServerExport = node.declarationList.declarations.some((d) => ts.isIdentifier(d.name) &&
|
||||
d.name.text === "server");
|
||||
if (isServerExport)
|
||||
return undefined; // Remove it
|
||||
}
|
||||
return ts.visitEachChild(node, visitor, context);
|
||||
};
|
||||
return ts.visitNode(rootNode, visitor);
|
||||
};
|
||||
};
|
||||
const result = ts.transform(sourceFile, [transformer]);
|
||||
const printer = ts.createPrinter();
|
||||
const strippedCode = printer.printFile(result.transformed[0]);
|
||||
const cleanSourceFile = ts.createSourceFile("clean.tsx", strippedCode, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
|
||||
// Simple reference check: if a named import isn't found in the text, drop it
|
||||
const cleanupTransformer = (context) => {
|
||||
return (rootNode) => {
|
||||
const visitor = (node) => {
|
||||
if (ts.isImportDeclaration(node)) {
|
||||
const clause = node.importClause;
|
||||
if (!clause)
|
||||
return node;
|
||||
// Handle named imports like { BunextPageProps, BunextPageServerFn }
|
||||
if (clause.namedBindings &&
|
||||
ts.isNamedImports(clause.namedBindings)) {
|
||||
const activeElements = clause.namedBindings.elements.filter((el) => {
|
||||
const name = el.name.text;
|
||||
// Check if the name appears anywhere else in the file
|
||||
const regex = new RegExp(`\\b${name}\\b`, "g");
|
||||
const matches = strippedCode.match(regex);
|
||||
return matches && matches.length > 1; // 1 for the import itself, >1 for usage
|
||||
});
|
||||
if (activeElements.length === 0)
|
||||
return undefined;
|
||||
return ts.factory.updateImportDeclaration(node, node.modifiers, ts.factory.updateImportClause(clause, clause.isTypeOnly, clause.name, ts.factory.createNamedImports(activeElements)), node.moduleSpecifier, node.attributes);
|
||||
}
|
||||
// Handle default imports like 'import BunSQLite'
|
||||
if (clause.name) {
|
||||
const name = clause.name.text;
|
||||
const regex = new RegExp(`\\b${name}\\b`, "g");
|
||||
const matches = strippedCode.match(regex);
|
||||
if (!matches || matches.length <= 1)
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
return ts.visitEachChild(node, visitor, context);
|
||||
};
|
||||
return ts.visitNode(rootNode, visitor);
|
||||
};
|
||||
};
|
||||
const finalResult = ts.transform(cleanSourceFile, [cleanupTransformer]);
|
||||
return printer.printFile(finalResult.transformed[0]);
|
||||
}
|
||||
Reference in New Issue
Block a user