Skip to content

Instantly share code, notes, and snippets.

@samselikoff
Last active May 6, 2021 14:18
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save samselikoff/38c59bdff8ca619a87c28e9c8d8b99f9 to your computer and use it in GitHub Desktop.
Save samselikoff/38c59bdff8ca619a87c28e9c8d8b99f9 to your computer and use it in GitHub Desktop.
withProse remark plugin that escapes `prose` for MDX components
let { withProse } = require("./src/remark/withProse");
let { withSyntaxHighlighting } = require("./src/remark/withSyntaxHighlighting");
let { withTableOfContents } = require("./src/remark/withTableOfContents");
let { withExamples } = require("./src/remark/withExamples");
let { withLayout } = require("./src/remark/withLayout");
let { withNextLinks } = require("./src/remark/withNextLinks");
let { VueLoaderPlugin } = require("vue-loader");
let path = require("path");
const withMDX = require("@next/mdx")({
options: {
remarkPlugins: [
withProse,
withSyntaxHighlighting,
withTableOfContents,
withNextLinks,
withExamples,
withLayout,
],
},
});
module.exports = withMDX({
pageExtensions: ["js", "jsx", "mdx"],
async redirects() {
return [
{ source: "/react", destination: "/", permanent: false },
{ source: "/vue", destination: "/", permanent: false },
];
},
webpack(config) {
config.resolveLoader.modules.push(
path.resolve(__dirname, "src", "loaders")
);
config.module.rules.push({
test: /\.(png|jpe?g|gif)$/i,
use: [
{
loader: "file-loader",
options: {
publicPath: "/_next",
name: "static/media/[name].[hash].[ext]",
},
},
],
});
config.module.rules.push({
test: /\.svg$/,
use: (resource) => {
// this is to make it so each svg import has a unique id. when importing
// an svg use the resource query to modify the id...
// example:
// import { ReactComponent as Icon } from "@/svgs/my-icon?uniqueId"
//
let svg = path.parse(resource.resource).name;
let id = resource.resourceQuery
? resource.resourceQuery.replace(/^\?/, "_")
: "_";
let prefix = `${svg}-${id}`;
return [
{
loader: "@svgr/webpack",
options: {
svgoConfig: {
plugins: [
{
prefixIds: {
prefix,
},
},
],
},
},
},
{
loader: "file-loader",
options: {
publicPath: "/_next",
name: "static/media/[name].[hash].[ext]",
},
},
];
},
});
config.module.rules.push({
test: /\.vue$/,
loader: "vue-loader",
});
config.plugins.push(new VueLoaderPlugin());
return config;
},
});
/*
Usage:
const withMDX = require("@next/mdx")({
options: {
remarkPlugins: [
withProse,
],
},
});
*/
const proseComponents = ["Heading"];
const isJsNode = (node) => {
return (
["jsx", "import", "export"].includes(node.type) &&
!/^<[a-z]+(>|\s)/.test(node.value) &&
!new RegExp(`^<(${proseComponents.join("|")})(>|\\s)`).test(node.value)
);
};
module.exports.withProse = () => {
return (tree) => {
let insideProse = false;
tree.children = tree.children.flatMap((node, i) => {
if (insideProse && isJsNode(node)) {
insideProse = false;
return [{ type: "jsx", value: "</div>" }, node];
}
if (!insideProse && !isJsNode(node)) {
insideProse = true;
return [
{ type: "jsx", value: '<div className="prose">' },
node,
...(i === tree.children.length - 1
? [{ type: "jsx", value: "</div>" }]
: []),
];
}
if (i === tree.children.length - 1 && insideProse) {
return [node, { type: "jsx", value: "</div>" }];
}
return [node];
});
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment