Open ericclemmons opened 5 years ago
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/quicklink@1.0.0/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:
.mdx
the same way.index.js
(or index.ts
) to that same file (by name), which would solve for https://github.com/mdx-js/mdx/issues/382.Put another way, blog/[post].mdx
would append the contents of blog/[post].ts
.
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
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
.
To support client-side rendering, I'm thinking:
${folder}/index.html
frompublic/index.html
.${folder}/index.js
from theindex.mdx
+index.tsx