Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client-side bundle #8

Open
ericclemmons opened this issue Jun 17, 2019 · 3 comments
Open

Client-side bundle #8

ericclemmons opened this issue Jun 17, 2019 · 3 comments

Comments

@ericclemmons
Copy link
Owner

To support client-side rendering, I'm thinking:

  1. Generate ${folder}/index.html from public/index.html.
  2. Generate ${folder}/index.js from the index.mdx + index.tsx
  3. Run parcel against it to replace it.
@ericclemmons
Copy link
Owner Author

I tried hard to make https://parceljs.org/ work, but the reality is that server-rendering this is easy (done!), but Parcel makes it very difficult to do a server+client build.

diff --git a/package.json b/package.json
index b1eb7c7..bb5ab4a 100644
--- a/package.json
+++ b/package.json
@@ -26,6 +26,7 @@
     "@types/microrouter": "^3.1.0",
     "@types/node": "^12.0.4",
     "@types/ora": "^3.2.0",
+    "@types/parcel-bundler": "^1.12.0",
     "@types/webpack-env": "^1.13.9",
     "np": "^5.0.3"
   },
@@ -42,6 +43,7 @@
     "micro": "^9.3.4",
     "microrouter": "^3.1.3",
     "ora": "^3.4.0",
+    "parcel-bundler": "^1.12.3",
     "prism-react-renderer": "^0.1.6",
     "react": "^16.8.6",
     "react-dom": "^16.8.6",
diff --git a/src/app.tsx b/src/app.tsx
index cdf5b8f..b7c5c63 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -1,13 +1,15 @@
+// import fse from "fs-extra";
 import { ServerResponse } from "http";
 import { send } from "micro";
 import { router, get } from "microrouter";
+import Bundler from "parcel-bundler";
 import path from "path";
 // @ts-ignore
 import serve from "serve-handler";

-import { defaultContentDir } from "./utils/defaults";
+import { defaultContentDir, defaultOutputDir } from "./utils/defaults";
 import getPage from "./utils/getPage";
-import renderPage from "./utils/renderPage";
+// import renderPage from "./utils/renderPage";

 export default router(
   get("(:folder)", async req => {
@@ -19,16 +21,77 @@ export default router(
     }

     const { folder } = req.params;
-    const page = await getPage(
-      path.join(defaultContentDir, folder, "index.mdx")
-    );
+    const pagePath = path.join(defaultContentDir, folder, "index.mdx");
+    const page = await getPage(pagePath);

     // Ignore folders without markup
     if (!page) {
       return;
     }

-    return renderPage(page);
+    const bundler = new Bundler(pagePath, {
+      cache: false,
+      hmr: false,
+      outDir: defaultOutputDir,
+      watch: true
+    });
+
+    // ! This is to test i it's a valid plugin
+    // require("./parcel-plugin");
+    bundler.addAssetType("mdx", require.resolve("./parcel-plugin"));
+
+    // ! This is to ensure we serve the file, not the folder
+    await bundler.bundle();
+
+    // ! This is a fallback for testing Parcel's output
+    await bundler.serve();
+
+    return;
+
+    // return renderPage(page);
+
+    // const targetHTML = path.join(
+    //   defaultOutputDir,
+    //   "__src",
+    //   folder,
+    //   "index.html"
+    // );
+
+    // const targetJS = path.join(defaultOutputDir, "__src", folder, "page.js");
+    // const output = await renderPage(page);
+
+    // await fse.mkdirp(path.dirname(targetHTML));
+    // await fse.writeFile(targetHTML, output, "utf8");
+
+    // await fse.copyFile(
+    //   targetPage,
+    //   path.join(defaultOutputDir, "__src", folder, "index.mdx")
+    // );
+
+    // fse.writeFile(targetJS, `console.log("${folder}");`, "utf8");
+
+    // const bundler = new Bundler(targetHTML, {
+    //   outDir: defaultOutputDir
+    // });
+
+    // console.log(await bundler.bundle());
+
+    // bundler.addAssetType("mdx", require.resolve("./parcel-plugin"));
+    // // Included for HRM
+    // require("./parcel-plugin");
+
+    // return new Promise((resolve, reject) => {
+    //   // @ts-ignore
+    //   bundler.middleware()(req, res, (err: Error, data: any) => {
+    //     if (err) {
+    //       return reject(err);
+    //     }
+
+    //     console.log({ data });
+
+    //     resolve(data);
+    //   });
+    // });
   }),

   get("/*", async (req, res) => {
@@ -37,7 +100,7 @@ export default router(
       res,
       {
         directoryListing: false,
-        public: "public",
+        public: defaultOutputDir,
         renderSingle: true
       },
       {
diff --git a/src/bin/mdx-site.tsx b/src/bin/mdx-site.tsx
index 572ae29..65cfc81 100644
--- a/src/bin/mdx-site.tsx
+++ b/src/bin/mdx-site.tsx
@@ -63,7 +63,19 @@ switch (command) {
     require("../build");
     break;
   case "start":
-    require("../server");
+    const Bundler = require("parcel-bundler");
+
+    const bundler = new Bundler("content/**/*.mdx", {
+      cache: false,
+      hmr: true,
+      outDir: defaultOutputDir,
+      watch: true
+    });
+
+    bundler.addAssetType("mdx", require.resolve("../parcel-plugin"));
+    bundler.serve();
+
+    // require("../server");
     break;
   default:
     throw new Error(
diff --git a/src/parcel-plugin.js b/src/parcel-plugin.js
new file mode 100644
index 0000000..fcb3835
--- /dev/null
+++ b/src/parcel-plugin.js
@@ -0,0 +1,87 @@
+// @ts-check
+
+const mdx = require("@mdx-js/mdx");
+const fse = require("fs-extra");
+
+const { default: getPage } = require("./utils/getPage");
+const { default: renderPage } = require("./utils/renderPage");
+
+const Asset = require("parcel-bundler/src/Asset");
+const HTMLAsset = require("parcel-bundler/src/assets/HTMLAsset");
+const path = require("path");
+
+module.exports = class MDXAsset extends HTMLAsset {
+  constructor(name, options) {
+    super(name, options);
+
+    // console.log(this);
+
+    //   this.type = "html";
+    //   this.hmrPageReload = true;
+  }
+
+  async parse(code) {
+    const page = await getPage(this.name);
+    const html = await renderPage(page);
+
+    return super.parse(html);
+  }
+
+  async generate() {
+    const parts = await super.generate();
+
+    // console.log({ parts });
+
+    console.log(this);
+    return parts;
+  }
+
+  // @ts-ignore
+  // async generate() {
+  //   const { options, resolver, ...rest } = this;
+  //   console.log("COnstructed", rest);
+
+  //   this.parentBundle = this;
+
+  //   const page = await getPage(this.name);
+
+  //   // console.log(this);
+
+  //   // this.addURLDependency(this.name, this.name, {
+  //   //   includedInParent: true
+  //   // });
+
+  //   return super.generate(await renderPage(page));
+  // if (this.type === "html") {
+  // }
+
+  // const pageJS = path.relative(
+  //   process.cwd(),
+  //   path.join(this.options.outDir, "index.js")
+  // );
+
+  // await fse.writeFile(
+  //   pageJS,
+  //   `
+  //   console.log(${JSON.stringify(folder)})
+  //   `,
+  //   "utf8"
+  // );
+
+  // this.addDependency(this.name, {
+  //   includedInParent: true
+  // });
+
+  // await fse.writeFile(
+  //   path.join(folder, "page.js"),
+  //   `
+  //   /* @jsx mdx */
+  //   import React from "react";
+  //   import { mdx } from "@mdx-js/react"
+
+  //   ${compiled}
+  //   `,
+  //   "utf8"
+  // );
+  // }
+};
diff --git a/src/utils/renderPage.tsx b/src/utils/renderPage.tsx
index 1ff9abb..37b6816 100644
--- a/src/utils/renderPage.tsx
+++ b/src/utils/renderPage.tsx
@@ -20,8 +20,8 @@ interface Page {
   readonly raw: string;
 }

-export default async function renderPage(page: Page) {
-  const { attributes, body, props } = page;
+export default async function renderPage(page: Page, variables = {}) {
+  const { attributes = {}, body, props } = page;
   const { title } = attributes;
   const components = await getComponents(defaultComponentsDir);

@@ -49,7 +49,8 @@ export default async function renderPage(page: Page) {
   const html = await renderTemplate({
     title: title || defaultTitle,
     description,
-    markup
+    markup,
+    ...variables
   });

   if (process.env.NODE_ENV !== "production") {
diff --git a/template/public/index.html b/template/public/index.html
index 4abedc5..4775fe4 100644
--- a/template/public/index.html
+++ b/template/public/index.html
@@ -14,6 +14,7 @@
   %MARKUP%
 </body>

+<script src="./index.mdx" type="application/javascript"></script>
 <script src="https://unpkg.com/[email protected]/dist/quicklink.umd.js"></script>

 </html>

Based on my research, I think the best possible solution would be similar to https://github.com/zeit/next.js/tree/canary/packages/next-mdx that would, instead:

Put another way, blog/[post].mdx would append the contents of blog/[post].ts.

@ericclemmons
Copy link
Owner Author

The bad news with this approach is that the intelligent usage of layouts & components would go away.

The good news is, this could possibly be an example in the Next.js repo, which could then be created via:

npx create-next-app --example mdx-site my-site

https://github.com/zeit/create-next-app#starting-from-nextjs-examples

@ericclemmons
Copy link
Owner Author

I did this as a Next.js app, but the JS payload was huge and creating <Snippet /> within a getInitialProps isn't JS-serializeable, so it doesn't work.

I think the next step would be to use webpack and bundle a page.js.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant