Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
zachleat committed Sep 23, 2022
0 parents commit d3eb611
Show file tree
Hide file tree
Showing 13 changed files with 428 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
package-lock.json
node_modules
130 changes: 130 additions & 0 deletions eleventyWebcPlugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
const path = require("path");
const { EleventyRenderPlugin } = require("@11ty/eleventy");
const EleventyRenderManager = EleventyRenderPlugin.RenderManager;

const CodeManager = require("./src/codeManager.js");
const WebCIncremental = require("./src/incremental.js");

/* TODO
* Use JavaScript functions or universal shortcodes as built-in components?
*/

module.exports = function(eleventyConfig, options = {}) {
options = Object.assign({
components: path.join(eleventyConfig.dir.includes, "/") + "**/*.webc"
}, options);

if(options.components) {
eleventyConfig.addWatchTarget(options.components);
}

eleventyConfig.addTemplateFormats("webc");

let renderManager = new EleventyRenderManager();
let cssManager = new CodeManager();
let jsManager = new CodeManager();

eleventyConfig.on("eleventy.before", () => {
cssManager.reset();
jsManager.reset();
});

function getCss(pageUrl, bucket = "default") {
return cssManager.getForPage(pageUrl, bucket);
}

function getJs(pageUrl, bucket = "default") {
return jsManager.getForPage(pageUrl, bucket);
}

// TODO
// eleventyConfig.addFilter("webcGetCSS", (url, bucket) => getCss(url, bucket));
// eleventyConfig.addFilter("webcGetJS", (url, bucket) => getJs(url, bucket));

let incremental = new WebCIncremental();

eleventyConfig.on("eleventy.layouts", layouts => {
incremental.setLayouts(layouts);
});

eleventyConfig.on("eleventy.before", () => {
incremental.setComponents(options.components);
});

eleventyConfig.addExtension("webc", {
outputFileExtension: "html",

init: async function() {
// For ESM in CJS
let e = await import("@11ty/webc");
incremental.setWebC(e.WebC);

if(incremental.needsComponents()) {
incremental.setComponents(options.components);
}
},

isIncrementalMatch: function (incrementalFilePath) {
// Eleventy layouts don’t appear directly in the WebC component graph, so we use the `eleventy.layouts` map here
if(incremental.isTemplateUsingLayout(this.inputPath, incrementalFilePath)) {
return true;
}

let {page, setup} = incremental.get(this.inputPath);
if(page && setup) {
if(page.getComponents(setup).includes(incrementalFilePath)) {
return true;
}
}

return false;
},

compile: async function(inputContent, inputPath) {
let page = incremental.add(inputContent, inputPath);

page.setHelper("getCSS", function(url, bucket) {
return getCss(url, bucket);
});

page.setHelper("getJS", function(url, bucket) {
return getJs(url, bucket);
});

page.setTransform("11ty", async function(content) {
let syntax = this["11ty:type"];
if(syntax) {
let fn = await renderManager.compile(content, syntax);
return renderManager.render(fn, this, {});
}
return content;
});

return async (data) => {
let setup = await page.setup({ data });
incremental.addSetup(inputPath, setup);

let { ast, serializer } = setup;
let { html, css, js, buckets } = await serializer.compile(ast);

cssManager.addToPage(data.page.url, css, "default");

if(buckets.css) {
for(let bucket in buckets.css) {
cssManager.addToPage(data.page.url, buckets.css[bucket], bucket);
}
}

jsManager.addToPage(data.page.url, js, "default");

if(buckets.js) {
for(let bucket in buckets.js) {
jsManager.addToPage(data.page.url, buckets.js[bucket], bucket);
}
}

return html;
};
}
});
}
44 changes: 44 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"name": "@11ty/eleventy-plugin-webc",
"version": "0.3.1",
"description": "WebC support for Eleventy adds for Single File Web Components",
"main": "eleventyWebcPlugin.js",
"scripts": {
"test": "npx ava"
},
"publishConfig": {
"access": "public"
},
"license": "MIT",
"engines": {
"node": ">=14.18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/11ty"
},
"keywords": [
"eleventy",
"eleventy-plugin",
"web-components",
"custom-elements"
],
"author": {
"name": "Zach Leatherman",
"email": "[email protected]",
"url": "https://zachleat.com/"
},
"ava": {
"failFast": true,
"files": [
"./test/*.js"
]
},
"dependencies": {
"@11ty/eleventy": "^2.0.0-canary.16",
"@11ty/webc": "^0.3.1"
},
"devDependencies": {
"ava": "^4.3.3"
}
}
33 changes: 33 additions & 0 deletions src/codeManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
class Manager {
constructor() {
this.reset();
}

reset() {
this.pages = {};
}

addToPage(pageUrl, code = [], bucket = "default") {
if(code.length === 0) {
return;
}

if(!this.pages[pageUrl]) {
this.pages[pageUrl] = {};
}
if(!this.pages[pageUrl][bucket]) {
this.pages[pageUrl][bucket] = new Set();
}

this.pages[pageUrl][bucket].add(code.join("\n"));
}

getForPage(pageUrl, bucket = "default") {
if(this.pages[pageUrl]) {
return Array.from(this.pages[pageUrl][bucket] || []).join("\n");
}
return "";
}
}

module.exports = Manager;
73 changes: 73 additions & 0 deletions src/incremental.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
class WebCIncremental {
constructor() {
this.pages = {};
this.setups = {};
this.globalComponentsMap = false;
this.components = {};
}

setWebC(cls) {
this.webc = cls;
}

needsComponents() {
return this.globalComponentsMap === false;
}

setComponents(glob) {
if(!this.webc) {
return;
}

let WebC = this.webc;
this.globalComponentsMap = WebC.getComponentsMap(glob);
// console.log( glob, Object.keys(this.globalComponentsMap).length );
}

setLayouts(layouts) {
this.layouts = layouts;
}

isTemplateUsingLayout(templateFilePath, layoutFilePath) {
if(this.layouts && this.layouts[layoutFilePath] && this.layouts[layoutFilePath].includes(templateFilePath)) {
return true;
}
return false;
}

add(inputContent, inputPath) {
let WebC = this.webc;
let page = new WebC();
page.setContent(inputContent, inputPath);

if(this.globalComponentsMap) {
page.defineComponents(this.globalComponentsMap);
}

this.pages[inputPath] = page;

return page;
}

addSetup(inputPath, setup) {
this.setups[inputPath] = setup;
this.components[inputPath] = setup.serializer.components;
}

get(inputPath) {
let setup = this.setups[inputPath];
let components = this.components[inputPath];

if(setup && components) {
setup.serializer.restorePreparsedComponents(components);
}

return {
page: this.pages[inputPath],
setup,
components,
}
}
}

module.exports = WebCIncremental;
17 changes: 17 additions & 0 deletions test/sample-1/_layouts/layout.webc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<title></title>
<style @html="this.getCSS(this.page.url)"></style>
<script @html="this.getJS(this.page.url)"></script>
</head>
<body>
<template webc:nokeep @html="this.content"></template>

<style @html="this.getCSS(this.page.url, 'defer')"></style>
<script @html="this.getJS(this.page.url, 'defer')"></script>
</body>
</html>
12 changes: 12 additions & 0 deletions test/sample-1/eleventy.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const EleventyWebcPlugin = require("../../eleventyWebcPlugin.js");

module.exports = function (eleventyConfig) {
eleventyConfig.addPlugin(EleventyWebcPlugin);

return {
dir: {
includes: "_includes",
layouts: "_layouts",
}
}
}
9 changes: 9 additions & 0 deletions test/sample-1/page.webc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
frontmatterdata: "HELLO FROM FRONT MATTER"
layout: layout.webc
---
<say-hello></say-hello>
<say-hello></say-hello>
WHO IS THIS
hi
<span @html="this.frontmatterdata"></span>
1 change: 1 addition & 0 deletions test/sample-2/_includes/component.webc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This is a component.
17 changes: 17 additions & 0 deletions test/sample-2/_layouts/layout.webc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<title></title>
<style @html="this.getCSS(this.page.url)"></style>
<script @html="this.getJS(this.page.url)"></script>
</head>
<body>
<template webc:nokeep @html="this.content"></template>

<style @html="this.getCSS(this.page.url, 'defer')"></style>
<script @html="this.getJS(this.page.url, 'defer')"></script>
</body>
</html>
15 changes: 15 additions & 0 deletions test/sample-2/eleventy.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const EleventyWebcPlugin = require("../../eleventyWebcPlugin.js");

module.exports = function (eleventyConfig) {
eleventyConfig.addPlugin(EleventyWebcPlugin, {
components: "test/sample-2/_includes/**/*.webc"
});
// eleventyConfig.addPlugin(EleventyWebcPlugin);

return {
dir: {
includes: "_includes",
layouts: "_layouts",
}
}
}
9 changes: 9 additions & 0 deletions test/sample-2/page-with-component.webc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
frontmatterdata: "HELLO FROM FRONT MATTER"
layout: layout.webc
---
<component></component>
<component></component>
WHO IS THIS
hi
<span @html="this.frontmatterdata"></span>
Loading

0 comments on commit d3eb611

Please sign in to comment.