Skip to content

Instantly share code, notes, and snippets.

@julrich
Created August 15, 2023 20:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save julrich/bfcb26bd35393a2818ade906b0315aa5 to your computer and use it in GitHub Desktop.
Save julrich/bfcb26bd35393a2818ade906b0315aa5 to your computer and use it in GitHub Desktop.
11ty + kickstartDS (Plugin)
const esbuild = require("esbuild");
const { sassPlugin } = require("esbuild-sass-plugin");
const { nodeExternalsPlugin } = require("esbuild-node-externals");
const { renderToStaticMarkup } = require("react-dom/server");
const lightningcss = require("lightningcss");
const browserslistToEsbuild = require("browserslist-to-esbuild");
const browserslist = require("browserslist");
const browsers = browserslist();
const esbuildTargets = browserslistToEsbuild(browsers);
const lightningcssTargets = lightningcss.browserslistToTargets(browsers);
const kds_exports = ["core", "base", "blog", "form", "content"].reduce(
(prev, curr) => {
try {
const exp = Object.keys(
require(`@kickstartds/${curr}/lib/exports.json`)
).map((e) => `node_modules/@kickstartds/${curr}/lib/` + e);
return prev.concat(exp);
} catch (e) {
return prev;
}
},
[]
);
function findClientScripts(name, clientScripts, inputs) {
if (inputs[name]?.imports.length) {
for (const i of inputs[name].imports) {
if (
i.path.endsWith(".js") &&
(i.path.endsWith(".client.js") || kds_exports.includes(i.path))
) {
clientScripts.push(i.path);
} else {
findClientScripts(i.path, clientScripts, inputs);
}
}
}
}
async function createPageContext(inputPath) {
return esbuild.context({
stdin: {
contents: `
import * as Page from "${inputPath}";
page = {
component: (data) => <Page.default {...data} />,
frontmatter: Page.frontmatter,
};
`,
resolveDir: process.cwd(),
loader: "jsx",
},
jsx: "automatic",
write: false,
bundle: true,
outdir: ".",
treeShaking: true,
loader: { ".svg": "dataurl" },
plugins: [
sassPlugin(),
nodeExternalsPlugin({
allowList: [
"@kickstartds/core",
"@kickstartds/base",
"@kickstartds/content",
"@kickstartds/form",
],
}),
],
metafile: true,
platform: "node",
define: {
"process.env.NODE_ENV": JSON.stringify("production"),
},
});
}
function bundlePage(result, inputPath) {
const page = new Function(
"require",
result.outputFiles[0].text + " return page;"
)(require);
const deps = Object.keys(result.metafile.inputs)
.filter(
(dep) =>
dep !== "<stdin>" &&
dep !== inputPath.slice(2) &&
!dep.startsWith("node_modules/")
)
.map((dep) => "./" + dep);
const clientScripts = [];
findClientScripts(inputPath.slice(2), clientScripts, result.metafile.inputs);
const { code: css } = lightningcss.transform({
code: Buffer.from(result.outputFiles[1]?.text || ""),
minify: true,
targets: lightningcssTargets,
});
return {
component: page.component,
data: page.frontmatter,
css,
clientScripts,
deps,
};
}
async function bundleClientScripts(clientScripts) {
const clientResult = await esbuild.build({
stdin: {
contents: clientScripts
.map((scriptPath) => `import "./${scriptPath}";`)
.join(""),
resolveDir: process.cwd(),
loader: "js",
},
write: false,
bundle: true,
outdir: ".",
minify: true,
treeShaking: true,
platform: "browser",
target: esbuildTargets,
define: {
"process.env.NODE_ENV": JSON.stringify("production"),
},
});
return clientResult.outputFiles[0].text;
}
/** @param {import("@11ty/eleventy").UserConfig} eleventyConfig */
module.exports = function kdsPlugin(eleventyConfig, options = {}) {
const { ignore = [] } = options;
const shouldIgnore = (inputPath) => {
for (const ignorePath of ignore) {
if (inputPath.startsWith(ignorePath)) {
return true;
}
}
return false;
};
eleventyConfig.addTemplateFormats("jsx");
eleventyConfig.addTemplateFormats("tsx");
const pageMap = new Map();
const pageContexts = new Map();
eleventyConfig.on("eleventy.after", ({ runMode }) => {
if (runMode === "build") {
for (const [, ctx] of pageContexts) {
ctx.dispose();
}
}
});
eleventyConfig.addExtension(["jsx", "tsx"], {
read: false,
getData: true,
compileOptions: {
// TODO: enable cache when deps watching is fixed
// https://github.com/11ty/eleventy/issues/2999
cache: false,
},
async getInstanceFromInputPath(inputPath) {
if (shouldIgnore(inputPath)) {
return { data: { eleventyExcludeFromCollections: true } };
}
if (!pageContexts.has(inputPath)) {
pageContexts.set(inputPath, await createPageContext(inputPath));
}
const ctx = pageContexts.get(inputPath);
const result = await ctx.rebuild();
const { data, ...page } = bundlePage(result, inputPath);
pageMap.set(inputPath, page);
return { data };
},
async compile(content, inputPath) {
if (shouldIgnore(inputPath)) return;
const { component, clientScripts, css, deps } = pageMap.get(inputPath);
this.addDependencies(inputPath, deps);
return async (data) => {
const html = renderToStaticMarkup(component(data));
const js = await bundleClientScripts(clientScripts);
return `${
css ? `<style>${css}</style>` : ""
} ${html} <script type="module">${js}</script>`;
};
},
});
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment