diff options
Diffstat (limited to 'packages')
-rw-r--r-- | packages/bun-framework-next/client.development.tsx | 69 | ||||
-rw-r--r-- | packages/bun-framework-next/empty.js (renamed from packages/bun-framework-next/index.js) | 0 | ||||
-rw-r--r-- | packages/bun-framework-next/fallback.development.tsx | 3 | ||||
-rw-r--r-- | packages/bun-framework-next/package.json | 45 | ||||
-rw-r--r-- | packages/bun-framework-next/page-loader.ts | 16 | ||||
-rw-r--r-- | packages/bun-framework-next/renderDocument.tsx | 124 | ||||
-rw-r--r-- | packages/bun-framework-next/server-polyfills.tsx | 26 | ||||
-rw-r--r-- | packages/bun-framework-next/server.development.tsx | 2 | ||||
-rw-r--r-- | packages/bun-framework-next/server.production.tsx | 0 | ||||
-rw-r--r-- | packages/bun-framework-next/text-encoder-polyfill.js | 4 | ||||
-rw-r--r-- | packages/bun-framework-next/tsconfig.json | 2 |
11 files changed, 150 insertions, 141 deletions
diff --git a/packages/bun-framework-next/client.development.tsx b/packages/bun-framework-next/client.development.tsx index f3d8d39e5..5d5e96763 100644 --- a/packages/bun-framework-next/client.development.tsx +++ b/packages/bun-framework-next/client.development.tsx @@ -1,10 +1,20 @@ +/// <reference types="react-dom/experimental" /> + globalThis.global = globalThis; globalThis.Bun_disableCSSImports = true; import * as React from "react"; + var onlyChildPolyfill = React.Children.only; -React.Children.only = function (children) { - if (children && typeof children === "object" && children.length == 1) { + +React.Children.only = function only( + children: React.ReactNode | React.ReactNode[] +) { + if ( + children && + typeof children === "object" && + (children as any).length == 1 + ) { return onlyChildPolyfill(children[0]); } @@ -23,9 +33,7 @@ import Router, { PrivateRouteInfo, } from "next/dist/shared/lib/router/router"; -const App = NextApp; - -import * as NextRouteLoader from "next/dist/client/route-loader"; +import NextRouteLoader from "next/dist/client/route-loader"; import { isDynamicRoute } from "next/dist/shared/lib/router/utils/is-dynamic"; import { urlQueryToSearchParams, @@ -41,6 +49,7 @@ import { createRouter, makePublicRouterInstance, } from "next/dist/client/router"; + export const emitter: MittEmitter<string> = mitt(); declare global { @@ -102,7 +111,8 @@ type RenderRouteInfo = PrivateRouteInfo & { const nextDataTag = document.getElementById("__NEXT_DATA__"); -const data: typeof window["__NEXT_DATA__"] = nextDataTag +// pages is added at runtime and doesn't exist in Next types +const data: NEXT_DATA & { pages: Record<string, string[]> } = nextDataTag ? JSON.parse(document.getElementById("__NEXT_DATA__")!.textContent!) : nextDataFromBunData(); @@ -116,12 +126,14 @@ const { buildId, assetPrefix, runtimeConfig, + // Todo, revist this constant when supporting dynamic() dynamicIds, isFallback, locale, locales, domainLocales, isPreview, + pages, } = data; const prefix: string = assetPrefix || ""; @@ -138,24 +150,19 @@ if (hasBasePath(asPath)) { asPath = delBasePath(asPath); } -export const pageLoader: PageLoader = new PageLoader( - buildId, - prefix, - data.pages -); +export const pageLoader: PageLoader = new PageLoader(buildId, prefix, pages); const headManager: { mountedInstances: Set<unknown>; updateHead: (head: JSX.Element[]) => void; } = initHeadManager(); -const appElement: HTMLElement | null = document.getElementById("__next"); export let router: Router; -let CachedApp: AppComponent = App, - onPerfEntry: (metric: any) => void; -export default function boot(EntryPointNamespace, loader) { - _boot(EntryPointNamespace).then(() => {}, false); +let CachedApp: AppComponent = null; + +export default function boot(EntryPointNamespace) { + _boot(EntryPointNamespace, false); } class Container extends React.Component<{ @@ -274,13 +281,17 @@ let reactRoot: any = null; const USE_REACT_18 = "hydrateRoot" in ReactDOM; export async function _boot(EntryPointNamespace, isError) { - NextRouteLoader.default.getClientBuildManifest = () => Promise.resolve({}); + NextRouteLoader.getClientBuildManifest = () => Promise.resolve({}); const PageComponent = EntryPointNamespace.default; const appScripts = globalThis.__NEXT_DATA__.pages["/_app"]; + // Type 'typeof App' is not assignable to type 'ComponentClass<AppProps, any>'. + // Construct signature return types 'App<any, any, any>' and 'Component<AppProps, any, any>' are incompatible. + // @ts-expect-error CachedApp = NextApp; + CachedComponent = PageComponent; if (appScripts && appScripts.length > 0) { let appSrc; @@ -293,6 +304,7 @@ export async function _boot(EntryPointNamespace, isError) { if (appSrc) { const AppModule = await import(appSrc); + console.assert( AppModule.default, appSrc + " must have a default export'd React component" @@ -316,9 +328,12 @@ export async function _boot(EntryPointNamespace, isError) { return render( Object.assign< {}, - Omit<RenderRouteInfo, "App" | "scroll">, - Pick<RenderRouteInfo, "App" | "scroll"> + Omit<RenderRouteInfo, "App" | "scroll" | "Component">, + Pick<RenderRouteInfo, "App" | "scroll" | "Component"> >({}, info, { + // If we don't have an info.Component, we may be shallow routing, + // fallback to current entry point + Component: info.Component || CachedComponent, App, scroll, }) @@ -332,7 +347,9 @@ export async function _boot(EntryPointNamespace, isError) { }); globalThis.next.router = router; + const domEl = document.querySelector("#__next"); + const reactEl = ( <TopLevelRender App={CachedApp} @@ -361,9 +378,9 @@ export async function _boot(EntryPointNamespace, isError) { } } -function TopLevelRender({ App, Component, props, scroll }) { +function TopLevelRender({ App, Component, props }) { return ( - <AppContainer scroll={scroll}> + <AppContainer> <App Component={Component} {...props}></App> </AppContainer> ); @@ -381,18 +398,14 @@ export function render(props) { } export function renderError(e) { - const reactEl = ( - <AppContainer> - <App Component={<div>UH OH!!!!</div>} pageProps={data.props}></App> - </AppContainer> - ); + const reactEl = <AppContainer>{null}</AppContainer>; if (USE_REACT_18) { if (!reactRoot) { const domEl = document.querySelector("#__next"); // Unlike with createRoot, you don't need a separate root.render() call here - reactRoot = ReactDOM.createRoot(domEl, reactEl); + reactRoot = ReactDOM.hydrateRoot(domEl, reactEl); } else { reactRoot.render(reactEl); } @@ -404,7 +417,7 @@ export function renderError(e) { } globalThis.next = { - version: "12.0.3", + version: "12.0.4", emitter, render, renderError, diff --git a/packages/bun-framework-next/index.js b/packages/bun-framework-next/empty.js index bbf5800ce..bbf5800ce 100644 --- a/packages/bun-framework-next/index.js +++ b/packages/bun-framework-next/empty.js diff --git a/packages/bun-framework-next/fallback.development.tsx b/packages/bun-framework-next/fallback.development.tsx index 54d31d5f5..460b5e165 100644 --- a/packages/bun-framework-next/fallback.development.tsx +++ b/packages/bun-framework-next/fallback.development.tsx @@ -1,5 +1,5 @@ import { insertStyleSheet } from "./page-loader"; -import type { FallbackMessageContainer } from "../../../src/api/schema"; +import type { FallbackMessageContainer } from "../../src/api/schema"; var once = false; function insertNextHeadCount() { @@ -65,6 +65,7 @@ function renderFallback({ router }: FallbackMessageContainer) { } export default function render(props: FallbackMessageContainer) { + // @ts-expect-error bun:error.js is real return import("/bun:error.js").then(({ renderFallbackError }) => { return renderFallback(props).then( () => { diff --git a/packages/bun-framework-next/package.json b/packages/bun-framework-next/package.json index 55c2ff930..884d27053 100644 --- a/packages/bun-framework-next/package.json +++ b/packages/bun-framework-next/package.json @@ -1,9 +1,30 @@ { "name": "bun-framework-next", "version": "12.0.5", - "main": "index.js", - "module": "index.js", + "main": "empty.js", + "module": "empty.js", "description": "Bun compatibility layer for Next.js v12.0.x", + "scripts": { + "test": "echo Worked", + "check": "tsc --noEmit" + }, + "author": "", + "license": "MIT", + "dependencies": { + "react-is": "^17.0.2", + "url-polyfill": "^1.1.12" + }, + "peerDependencies": { + "next": "^12.0.0" + }, + "devDependencies": { + "@types/react": "^17.0.34", + "@types/react-dom": "^17.0.11", + "next": "^12.0.0", + "react-dom": "^17.0.2", + "react-refresh": "^0.10.0", + "typescript": "^4.4.4" + }, "framework": { "displayName": "Next.js", "static": "public", @@ -69,24 +90,10 @@ } }, "production": { - "client": "client.production.tsx", - "server": "server.production.tsx", - "fallback": "fallback.production.tsx", + "client": "empty.js", + "server": "empty.js", + "fallback": "empty.js", "css": "onimportcss" } - }, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "MIT", - "dependencies": { - "react-is": "^17.0.2" - }, - "peerDependencies": { - "next": "^12.0.0" - }, - "devDependencies": { - "react-refresh": "^0.10.0" } } diff --git a/packages/bun-framework-next/page-loader.ts b/packages/bun-framework-next/page-loader.ts index 2a8b402ad..3b31387d8 100644 --- a/packages/bun-framework-next/page-loader.ts +++ b/packages/bun-framework-next/page-loader.ts @@ -1,4 +1,6 @@ -import NextPageLoader from "next/dist/client/page-loader"; +import NextPageLoader, { + GoodPageCache as NextGoodPageCache, +} from "next/dist/client/page-loader"; import getAssetPathFromRoute from "next/dist/shared/lib/router/utils/get-asset-path-from-route"; export function insertStyleSheet(url: string) { @@ -10,8 +12,9 @@ export function insertStyleSheet(url: string) { const link = document.createElement("link"); link.rel = "stylesheet"; - link.onload = () => resolve(); - link.onerror = () => reject(); + // marking this resolve as void seems to break other things + link.onload = resolve; + link.onerror = reject; link.href = url; @@ -19,6 +22,11 @@ export function insertStyleSheet(url: string) { }); } +interface GoodPageCache extends NextGoodPageCache { + __N_SSG: boolean; + __N_SSP: boolean; +} + export default class PageLoader extends NextPageLoader { constructor(_, __, pages) { super(_, __); @@ -70,7 +78,7 @@ export default class PageLoader extends NextPageLoader { }; prefetch() { - return Promise.resolve({}); + return Promise.resolve(); } async loadPage(route: string): Promise<GoodPageCache> { diff --git a/packages/bun-framework-next/renderDocument.tsx b/packages/bun-framework-next/renderDocument.tsx index 59e18fbe7..9cc6654c7 100644 --- a/packages/bun-framework-next/renderDocument.tsx +++ b/packages/bun-framework-next/renderDocument.tsx @@ -1,4 +1,4 @@ -import * as App from "next/app"; +import App from "next/app"; import { AmpStateContext } from "next/dist/shared/lib/amp-context"; import { HeadManagerContext } from "next/dist/shared/lib/head-manager-context"; import Loadable from "next/dist/shared/lib/loadable"; @@ -18,16 +18,22 @@ import { RenderPageResult, HtmlContext, } from "next/dist/shared/lib/utils"; +import { RenderOpts } from "next/dist/server/render"; import * as NextDocument from "next/document"; import * as ReactDOMServer from "react-dom/server.browser"; import * as React from "react"; import * as ReactIs from "react-is"; -import { BODY_RENDER_TARGET } from "next/constants"; + +// This constant was removed from Next, Is it doing anything? +const BODY_RENDER_TARGET = "__NEXT_BODY_RENDER_TARGET__"; const dev = process.env.NODE_ENV === "development"; type ParsedUrlQuery = Record<string, string | string[]>; +// @ts-expect-error TS doesn't understand that Bun is a global +const origin: string = Bun.origin; + const isJSFile = (file: string) => file.endsWith(".js") || file.endsWith(".jsx") || @@ -35,40 +41,11 @@ const isJSFile = (file: string) => file.endsWith(".ts") || file.endsWith(".tsx"); -const notImplementedProxy = (base) => - new Proxy( - {}, - { - deleteProperty: function (target, prop) { - return undefined; - }, - enumerate: function (oTarget, sKey) { - return [].entries(); - }, - ownKeys: function (oTarget, sKey) { - return [].values(); - }, - has: function (oTarget, sKey) { - return false; - }, - defineProperty: function (oTarget, sKey, oDesc) { - return undefined; - }, - getOwnPropertyDescriptor: function (oTarget, sKey) { - return undefined; - }, - get(this, prop) { - throw new ReferenceError( - `${base} is not available for this environment.` - ); - }, - set(this, prop, value) { - throw new ReferenceError( - `${base} is not available for this environment.` - ); - }, - } - ); +type DocumentFiles = { + sharedFiles: readonly string[]; + pageFiles: readonly string[]; + allFiles: readonly string[]; +}; function getScripts(files: DocumentFiles) { const { context, props } = this; @@ -147,6 +124,9 @@ function renderDocument( disableOptimizedLoading, }: RenderOpts & { props: any; + // + page: string; + // docComponentsRendered: DocumentProps["docComponentsRendered"]; docProps: DocumentInitialProps; pathname: string; @@ -222,7 +202,11 @@ function renderDocument( "<!DOCTYPE html>" + ReactDOMServer.renderToStaticMarkup( <AmpStateContext.Provider value={ampState}> + {/* HTMLContextProvider expects useMainContent */} + {/* @ts-expect-error */} <HtmlContext.Provider value={htmlProps}> + {/* Document doesn't expect useMaybeDeferContent */} + {/* @ts-expect-error */} <Document {...htmlProps} {...docProps}></Document> </HtmlContext.Provider> </AmpStateContext.Provider> @@ -359,7 +343,8 @@ export async function render({ routeNames: string[]; request: Request; }): Promise<Response> { - const { default: Component, getStaticProps = null } = PageNamespace || {}; + const { default: Component } = PageNamespace || {}; + const getStaticProps = (PageNamespace as any)?.getStaticProps || null; const { default: AppComponent_ } = AppNamespace || {}; var query = Object.assign({}, route.query); @@ -371,7 +356,7 @@ export async function render({ for (let i = 0; i < routeNames.length; i++) { const filePath = routePaths[i]; const name = routeNames[i]; - pages[name] = [Bun.origin + filePath]; + pages[name] = [origin + filePath]; } if (appStylesheets.length > 0) { @@ -387,11 +372,8 @@ export async function render({ pages["/_app"] = []; } - const AppComponent = AppComponent_ || App.default; - const Document = - (DocumentNamespace && DocumentNamespace.default) || NextDocument.default; - // Document.Html.prototype.getScripts = getScripts; - // } + const AppComponent = AppComponent_ || App; + const Document = (DocumentNamespace as any)?.default || NextDocument.default; const callMiddleware = async (method: string, args: any[], props = false) => { let results: any = props ? {} : []; @@ -447,14 +429,16 @@ export async function render({ delete query.__nextLocale; delete query.__nextDefaultLocale; - const isSSG = !!getStaticProps; - const isBuildTimeSSG = isSSG && false; + // const isSSG = !!getStaticProps; + const defaultAppGetInitialProps = App.getInitialProps === (App as any).origGetInitialProps; const hasPageGetInitialProps = !!(Component as any).getInitialProps; const pageIsDynamic = route.kind === "dynamic"; + const isPreview = false; const isAutoExport = false; + const nextExport = isAutoExport || isFallback; if (isAutoExport || isFallback) { // // remove query values except ones that will be set during export @@ -476,7 +460,6 @@ export async function render({ <meta name="viewport" content="width=device-width" />, ]; - const nextExport = isAutoExport || isFallback; const reactLoadableModules: string[] = []; var scriptLoader = {}; const AppContainer = ({ children }: any) => ( @@ -504,6 +487,7 @@ export async function render({ </RouterContext.Provider> ); + // Todo: Double check this when adding support for dynamic() await Loadable.preloadAll(); // Make sure all dynamic imports are loaded const router = new ServerRouter( @@ -514,7 +498,7 @@ export async function render({ isFallback: isFallback, }, true, - Bun.origin, + origin, null, [], // renderOpts.locales, null, //renderOpts.defaultLocale, @@ -541,7 +525,7 @@ export async function render({ ); }, defaultGetInitialProps: async ( - docCtx: DocumentContext + docCtx: NextDocument.DocumentContext ): Promise<DocumentInitialProps> => { const enhanceApp = (AppComp: any) => { return (props: any) => <AppComp {...props} />; @@ -553,7 +537,7 @@ export async function render({ }, }; - var props = await loadGetInitialProps(AppComponent, { + var props: any = await loadGetInitialProps(AppComponent, { AppTree: ctx.AppTree, Component, router, @@ -565,6 +549,7 @@ export async function render({ // We don't call getServerSideProps on clients. // This isn't correct. // We don't call getServerSideProps on clients. + // @ts-expect-error const getServerSideProps = PageNamespace.getServerSideProps; var responseHeaders: Headers; @@ -661,6 +646,7 @@ export async function render({ const renderToString = ReactDOMServer.renderToString; const ErrorDebug = null; + props.pageProps = pageProps; const renderPage: RenderPage = ( @@ -668,12 +654,7 @@ export async function render({ ): RenderPageResult | Promise<RenderPageResult> => { if (ctx.err && ErrorDebug) { const htmlOrPromise = renderToString(<ErrorDebug error={ctx.err} />); - return typeof htmlOrPromise === "string" - ? { html: htmlOrPromise, head } - : htmlOrPromise.then((html) => ({ - html, - head, - })); + return { html: htmlOrPromise, head }; } if (dev && (props.router || props.Component)) { @@ -683,6 +664,8 @@ export async function render({ } const { App: EnhancedApp, Component: EnhancedComponent } = + // Argument of type 'NextComponentType<any, {}, {}> | typeof App' is not assignable to parameter of type 'AppType'. + // @ts-expect-error enhanceComponents(options, AppComponent, Component); const htmlOrPromise = renderToString( @@ -695,12 +678,8 @@ export async function render({ /> </AppContainer> ); - return typeof htmlOrPromise === "string" - ? { html: htmlOrPromise, head } - : htmlOrPromise.then((html) => ({ - html, - head, - })); + + return { html: htmlOrPromise, head }; }; const documentCtx = { ...ctx, renderPage }; @@ -719,35 +698,31 @@ export async function render({ const renderOpts = { params: route.params, }; - // renderOpts.params = _params || params; - // parsedUrl.pathname = denormalizePagePath(parsedUrl.pathname!); - // renderOpts.resolvedUrl = formatUrl({ - // ...parsedUrl, - // query: origQuery, - // }); const docComponentsRendered: DocumentProps["docComponentsRendered"] = {}; - const isPreview = false; - let html = renderDocument(Document, { docComponentsRendered, ...renderOpts, disableOptimizedLoading: false, - canonicalBase: Bun.origin, + canonicalBase: origin, buildManifest: { devFiles: [], allFiles: [], polyfillFiles: [], lowPriorityFiles: [], - pages: pages, + // buildManifest doesn't expect pages, even though its used + // @ts-expect-error + pages, }, // Only enabled in production as development mode has features relying on HMR (style injection for example) + // @ts-expect-error unstable_runtimeJS: true, // process.env.NODE_ENV === "production" // ? pageConfig.unstable_runtimeJS // : undefined, // unstable_JsPreload: pageConfig.unstable_JsPreload, + // @ts-expect-error unstable_JsPreload: true, dangerousAsPath: router.asPath, ampState: undefined, @@ -770,11 +745,12 @@ export async function render({ appGip: !defaultAppGetInitialProps ? true : undefined, devOnlyCacheBusterQueryString: "", scriptLoader, - isPreview: isPreview === true ? true : undefined, - autoExport: isAutoExport === true ? true : undefined, - nextExport: nextExport === true ? true : undefined, + isPreview: isPreview, + autoExport: nextExport === true ? true : undefined, + nextExport: nextExport, useMaybeDeferContent, }); + // __NEXT_BODY_RENDER_TARGET__ const bodyRenderIdx = html.indexOf(BODY_RENDER_TARGET); html = html.substring(0, bodyRenderIdx) + diff --git a/packages/bun-framework-next/server-polyfills.tsx b/packages/bun-framework-next/server-polyfills.tsx index 474ee6ec9..f0a2b5c3a 100644 --- a/packages/bun-framework-next/server-polyfills.tsx +++ b/packages/bun-framework-next/server-polyfills.tsx @@ -1,28 +1,26 @@ globalThis.global = globalThis; import { Buffer } from "buffer"; - -globalThis.Buffer = Buffer; - +import { URL } from "url-polyfill"; +import { TextEncoder, TextDecoder } from "./text-encoder-polyfill"; import * as React from "react"; -class URL { - constructor(base, source) { - this.pathname = source; - this.href = base + source; - } -} -var onlyChildPolyfill = React.Children.only; +const onlyChildPolyfill = React.Children.only; + React.Children.only = function (children) { - if (children && typeof children === "object" && children.length == 1) { + if ( + children && + typeof children === "object" && + (children as any).length == 1 + ) { return onlyChildPolyfill(children[0]); } return onlyChildPolyfill(children); }; -globalThis.URL = URL; - -import { TextEncoder, TextDecoder } from "./text-encoder-polyfill"; +globalThis.Buffer = Buffer; +globalThis.URL = URL; +// @ts-expect-error encodeInto is missing in our polyfill globalThis.TextEncoder ||= TextEncoder; globalThis.TextDecoder ||= TextDecoder; diff --git a/packages/bun-framework-next/server.development.tsx b/packages/bun-framework-next/server.development.tsx index 96ba55812..4ac2621e8 100644 --- a/packages/bun-framework-next/server.development.tsx +++ b/packages/bun-framework-next/server.development.tsx @@ -26,6 +26,8 @@ import(Bun.routesDir + "_document").then( DocumentLoaded = true; }, (err) => { + // ResolveError is defined outside of bun-framework-next in ../../src/runtime/errors + // @ts-expect-error if (err instanceof ResolveError) { DocumentLoaded = true; } else { diff --git a/packages/bun-framework-next/server.production.tsx b/packages/bun-framework-next/server.production.tsx deleted file mode 100644 index e69de29bb..000000000 --- a/packages/bun-framework-next/server.production.tsx +++ /dev/null diff --git a/packages/bun-framework-next/text-encoder-polyfill.js b/packages/bun-framework-next/text-encoder-polyfill.js index 3dd95009f..01c7cbbe8 100644 --- a/packages/bun-framework-next/text-encoder-polyfill.js +++ b/packages/bun-framework-next/text-encoder-polyfill.js @@ -35,6 +35,10 @@ Object.defineProperty(FastTextEncoder.prototype, "encoding", { value: "utf-8", }); +Object.defineProperty(FastTextEncoder.prototype, "encodeInto", { + value: "", +}); + /** * @param {string} string * @param {{stream: boolean}=} options diff --git a/packages/bun-framework-next/tsconfig.json b/packages/bun-framework-next/tsconfig.json index d14767c9f..09f55d73c 100644 --- a/packages/bun-framework-next/tsconfig.json +++ b/packages/bun-framework-next/tsconfig.json @@ -16,6 +16,6 @@ "baseUrl": ".", "paths": {} }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "empty.js"], "exclude": ["node_modules"] } |