oven-sh / bun

Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one
https://bun.sh
Other
73.11k stars 2.67k forks source link

Single file executable does not appear to work with plugins (at least not MDX) #13530

Open JustinTulloss opened 2 weeks ago

JustinTulloss commented 2 weeks ago

What version of Bun is running?

1.1.26+0a37423ba

What platform is your computer?

Darwin 23.6.0 arm64 arm

What steps can reproduce the bug?

The following files produce a different output when run directly vs when run as a compiled single-file executable.

preload-plugins.ts

import { plugin, type BunPlugin } from "bun";
import mdx from "@mdx-js/esbuild";

console.log("Loading plugins...");
plugin(mdx() as unknown as BunPlugin);

bunfig.toml

preload=["./preload-plugins.ts"]

index.tsx

import { embeddedFiles, serve } from "bun";
import { renderToStaticMarkup } from "react-dom/server"

import Post from "./test.mdx";

console.log("Hello via Bun!");
console.log(embeddedFiles);
console.log(renderToStaticMarkup(<Post />));

What is the expected behavior?

The built single file executable should output the same thing as running bun run index.tsx, except with different embedded plugins

Loading plugins...
Hello via Bun!
[]
<h1>Rendered contents of test.mdx</h1>

What do you see instead?

Hello via Bun!
[
  Blob (274 bytes) {
    name: "test-z9gtn511.mdx",
    type: "text/mdx"
  }
]
2729 | // https://infra.spec.whatwg.org/#ascii-tab-or-newline
2730 | // https://infra.spec.whatwg.org/#ascii-tab-or-newline
2731 | // https://infra.spec.whatwg.org/#ascii-tab-or-newline
2732 | // https://infra.spec.whatwg.org/#ascii-tab-or-newline
2733 | // https://infra.spec.whatwg.org/#ascii-tab-or-newline
2734 | // https://infra.spec.whatwg.org/#ascii-tab-or-newline
                   ^
error: Invalid tag: /$bunfs/root/test-z9gtn511.mdx

Additional information

You can get further by shlepping the bunfig.toml around with the executable, and even further by copying the executable along with bunfig.toml and preload-plugins.tsx but the build process still doesn't include the imports specified in preload-plugins.tsx so eventually it crashes.

JustinTulloss commented 2 weeks ago

It does appear that you can get the necessary dependencies included by including preload-plugins.ts as one of the files that gets compiled, but the bunfig.toml includes a relative path that doesn't resolve within the SFE environment and the bunfig.toml file does not appear to be read if you include it in the SFE src args.

Jarred-Sumner commented 2 weeks ago

I think the ideal fix here is for us to implement compile: true in Bun.build so that plugins are run ahead-of-time instead of at runtime.

If you want to inline the contents of .mdx files at compile time, you can do this:

import { mdx } from "./render.tsx" with { type: "macro" };

const Post = mdx("./test1.mdx");

console.log(Post);

and then render.tsx:

import { compile } from "@mdx-js/mdx";
import { readFileSync } from "fs";
import path from "path";
import React from "react";
import { renderToStaticMarkup } from "react-dom/server";
async function renderMDXToStaticMarkup(path) {
  const mdxContent = readFileSync(path, "utf8");

  const blob = await compile(mdxContent, {
    outputFormat: "program",
  }).then((code) => {
    return new Blob([code.value], { type: "application/javascript" });
  });
  const url = URL.createObjectURL(blob);
  try {
    const component = await import(url);
    return renderToStaticMarkup(React.createElement(component.default));
  } finally {
    URL.revokeObjectURL(url);
  }
}

export function mdx(filePath: string) {
  return renderMDXToStaticMarkup(path.join(process.cwd(), filePath));
}

If you actually do want to run the MDX plugin at runtime within the standalone executable, then something like this should work:

import { serve } from "bun";
import { renderToStaticMarkup } from "react-dom/server";
import { plugin, type BunPlugin } from "bun";
import mdx from "@mdx-js/esbuild";
import path from 'path';
console.log("Loading plugins...");
plugin(mdx() as unknown as BunPlugin);

const PostNamespace = await import(
  path.join(process.cwd(), "./test.mdx")
);
const Post = PostNamespace.default;
console.log(Post);
console.log("Hello via Bun!");
console.log(renderToStaticMarkup(<Post />));
JustinTulloss commented 2 weeks ago

Another potential solution would be to add a —plugin flag to the build step to include those plugins. However there’s still the awkwardness of telling the executable when to run them.

I’ll precompile the mdx files, that should work for me!

Thank you

On Tue, Aug 27, 2024 at 4:33 AM Jarred Sumner @.***> wrote:

I think the ideal fix here is for us to implement compile: true so that plugins are run ahead-of-time instead of at runtime.

If you want to inline the contents of .mdx files at compile time, you can do this:

import { mdx } from "./render.tsx" with { type: "macro" }; const Post = mdx("./test1.mdx"); console.log(Post);

and then render.tsx:

import { compile } from @.***/mdx";import { readFileSync } from "fs";import path from "path";import React from "react";import { renderToStaticMarkup } from "react-dom/server";async function renderMDXToStaticMarkup(path) { const mdxContent = readFileSync(path, "utf8");

const blob = await compile(mdxContent, { outputFormat: "program", }).then((code) => { return new Blob([code.value], { type: "application/javascript" }); }); const url = URL.createObjectURL(blob); try { const component = await import(url); return renderToStaticMarkup(React.createElement(component.default)); } finally { URL.revokeObjectURL(url); }} export function mdx(filePath: string) { return renderMDXToStaticMarkup(path.join(process.cwd(), filePath));}

If you actually do want to run the MDX plugin at runtime within the standalone executable, then something like this should work:

import { serve } from "bun";import { renderToStaticMarkup } from "react-dom/server";import { plugin, type BunPlugin } from "bun";import mdx from @.***/esbuild";import path from 'path';console.log("Loading plugins...");plugin(mdx() as unknown as BunPlugin); const PostNamespace = await import( path.join(process.cwd(), "./test.mdx"));const Post = PostNamespace.default;console.log(Post);console.log("Hello via Bun!");console.log(renderToStaticMarkup());

— Reply to this email directly, view it on GitHub https://github.com/oven-sh/bun/issues/13530#issuecomment-2312312260, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAFZNTVP6CTBESAIUFB42TZTRPXHAVCNFSM6AAAAABNC4DBQ2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDGMJSGMYTEMRWGA . You are receiving this because you authored the thread.Message ID: @.***>