├── .gitignore ├── .npmignore ├── test ├── render-plugin │ ├── webc │ │ └── my-component.webc │ ├── page.md │ └── eleventy.config.mjs ├── sample-2 │ ├── _includes │ │ └── component.webc │ ├── page.webc │ ├── eleventy.config.js │ └── _layouts │ │ └── layout.webc ├── default-components │ ├── _includes │ │ └── say-hello.webc │ ├── _components │ │ └── say-hello.webc │ ├── page.webc │ └── eleventy.config.js ├── nested-layouts │ ├── _components │ │ ├── reprocess-me.webc │ │ └── say-hello.webc │ ├── page.webc │ ├── _includes │ │ ├── layout.webc │ │ └── base.webc │ └── eleventy.config.js ├── raw-layout-html │ ├── _includes │ │ ├── reprocess-me.webc │ │ └── say-hello.webc │ ├── page.webc │ ├── eleventy.config.js │ └── _layouts │ │ └── layout.webc ├── shortcodes-issue-16 │ ├── _components │ │ └── say-hello.webc │ ├── page.webc │ └── eleventy.config.js ├── components-in-layouts │ ├── page1.webc │ ├── _components │ │ ├── inner.webc │ │ └── outer.webc │ ├── page2.webc │ ├── page3.webc │ ├── _includes │ │ ├── layout.webc │ │ └── base.webc │ └── eleventy.config.js ├── permalink-string │ ├── page.webc │ ├── page2.webc │ └── eleventy.config.js ├── sample-page-global-components │ ├── _includes │ │ └── page1-components │ │ │ └── say-hello.webc │ ├── eleventy.config.js │ ├── page.webc │ └── _layouts │ │ └── layout.webc ├── webc-component-in-layout │ ├── page.liquid │ ├── _components │ │ └── twitter-share.webc │ ├── _includes │ │ └── layout.liquid │ └── eleventy.config.mjs ├── sample-html-preprocess │ ├── _includes │ │ └── say-hello.webc │ ├── page.html │ └── eleventy.config.js ├── sample-non-webc-layout │ ├── _includes │ │ └── say-hello.webc │ ├── page.webc │ ├── eleventy.config.js │ └── _layouts │ │ └── layout.liquid ├── custom-permalink-issue-32 │ ├── page.webc │ └── eleventy.config.js ├── sample-page-global-components-relative-to-inputpath │ ├── _includes │ │ └── page1-components │ │ │ └── say-hello.webc │ ├── page.webc │ ├── eleventy.config.js │ └── _layouts │ │ └── layout.webc ├── uid-leftovers │ ├── page.webc │ ├── _layouts │ │ └── layout.webc │ └── eleventy.config.js ├── custom-permalink-not-dynamic-issue-32 │ ├── page.webc │ └── eleventy.config.js ├── sample-transform │ ├── _includes │ │ └── say-hello.webc │ ├── page.liquid │ └── eleventy.config.js ├── bundler-to-file │ └── index.webc ├── custom-js-front-matter │ ├── page.liquid │ └── eleventy.config.js ├── generic.eleventy.config.js ├── bundler-helpers │ ├── eleventy.config.js │ └── index.webc ├── custom-permalink │ ├── eleventy.config.js │ └── page.webc ├── sample-1 │ ├── page.webc │ ├── eleventy.config.js │ └── _layouts │ │ └── layout.webc ├── sample-universal-helpers │ ├── page.webc │ └── eleventy.config.js ├── script-and-style-buckets │ ├── page.webc │ ├── _components │ │ └── my-button.webc │ ├── eleventy.config.mjs │ └── _layouts │ │ └── layout.webc ├── sample-permalink-false-webc │ ├── page.webc │ └── eleventy.config.js ├── sample-permalink-false │ ├── page.liquid │ └── eleventy.config.js ├── test-chdir.mjs └── test.mjs ├── .vscode └── settings.json ├── .editorconfig ├── .github └── workflows │ ├── ci.yml │ └── release.yml ├── package.json ├── src ├── eleventyWebcTransform.js ├── dynamicScript.js └── eleventyWebcTemplate.js ├── README.md └── eleventyWebcPlugin.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test 2 | .* -------------------------------------------------------------------------------- /test/render-plugin/webc/my-component.webc: -------------------------------------------------------------------------------- 1 | My component -------------------------------------------------------------------------------- /test/sample-2/_includes/component.webc: -------------------------------------------------------------------------------- 1 | This is a component. -------------------------------------------------------------------------------- /test/default-components/_includes/say-hello.webc: -------------------------------------------------------------------------------- 1 | INCLUDES DIR -------------------------------------------------------------------------------- /test/nested-layouts/_components/reprocess-me.webc: -------------------------------------------------------------------------------- 1 | REPROCESSED -------------------------------------------------------------------------------- /test/raw-layout-html/_includes/reprocess-me.webc: -------------------------------------------------------------------------------- 1 | REPROCESSED -------------------------------------------------------------------------------- /test/default-components/_components/say-hello.webc: -------------------------------------------------------------------------------- 1 | COMPONENTS DIR -------------------------------------------------------------------------------- /test/shortcodes-issue-16/_components/say-hello.webc: -------------------------------------------------------------------------------- 1 | COMPONENTS DIR -------------------------------------------------------------------------------- /test/components-in-layouts/page1.webc: -------------------------------------------------------------------------------- 1 | --- 2 | layout: layout.webc 3 | --- 4 | Page -------------------------------------------------------------------------------- /test/permalink-string/page.webc: -------------------------------------------------------------------------------- 1 | --- 2 | permalink: "index.html" 3 | --- 4 | Hello -------------------------------------------------------------------------------- /test/sample-page-global-components/_includes/page1-components/say-hello.webc: -------------------------------------------------------------------------------- 1 | HELLO -------------------------------------------------------------------------------- /test/components-in-layouts/_components/inner.webc: -------------------------------------------------------------------------------- 1 | Test 2 | -------------------------------------------------------------------------------- /test/components-in-layouts/_components/outer.webc: -------------------------------------------------------------------------------- 1 | Test 2 | -------------------------------------------------------------------------------- /test/components-in-layouts/page2.webc: -------------------------------------------------------------------------------- 1 | --- 2 | layout: layout.webc 3 | --- 4 | Other page -------------------------------------------------------------------------------- /test/permalink-string/page2.webc: -------------------------------------------------------------------------------- 1 | --- 2 | permalink: "`index2.html`" 3 | --- 4 | Hello -------------------------------------------------------------------------------- /test/webc-component-in-layout/page.liquid: -------------------------------------------------------------------------------- 1 | --- 2 | layout: layout.liquid 3 | --- 4 | Hello -------------------------------------------------------------------------------- /test/sample-html-preprocess/_includes/say-hello.webc: -------------------------------------------------------------------------------- 1 | HELLO -------------------------------------------------------------------------------- /test/sample-non-webc-layout/_includes/say-hello.webc: -------------------------------------------------------------------------------- 1 | HELLO 2 | -------------------------------------------------------------------------------- /test/custom-permalink-issue-32/page.webc: -------------------------------------------------------------------------------- 1 | --- 2 | frontmatterdata: "HELLO FROM FRONT MATTER" 3 | --- 4 | -------------------------------------------------------------------------------- /test/sample-page-global-components-relative-to-inputpath/_includes/page1-components/say-hello.webc: -------------------------------------------------------------------------------- 1 | HELLO -------------------------------------------------------------------------------- /test/uid-leftovers/page.webc: -------------------------------------------------------------------------------- 1 | --- 2 | layout: layout.webc 3 | --- 4 | -------------------------------------------------------------------------------- /test/sample-html-preprocess/page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | WHO IS THIS 4 | hi -------------------------------------------------------------------------------- /test/custom-permalink-not-dynamic-issue-32/page.webc: -------------------------------------------------------------------------------- 1 | --- 2 | frontmatterdata: "HELLO FROM FRONT MATTER" 3 | --- 4 | -------------------------------------------------------------------------------- /test/sample-transform/_includes/say-hello.webc: -------------------------------------------------------------------------------- 1 | HELLO -------------------------------------------------------------------------------- /test/components-in-layouts/page3.webc: -------------------------------------------------------------------------------- 1 | No layouts here 2 | 3 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.trimAutoWhitespace": false, 3 | "files.trimTrailingWhitespaceInRegexAndStrings": false 4 | } -------------------------------------------------------------------------------- /test/render-plugin/page.md: -------------------------------------------------------------------------------- 1 | # Hello 2 | 3 | {% renderTemplate "webc" %} 4 | 5 | {% endrenderTemplate %} -------------------------------------------------------------------------------- /test/nested-layouts/page.webc: -------------------------------------------------------------------------------- 1 | --- 2 | layout: layout.webc 3 | --- 4 | Testing 5 | -------------------------------------------------------------------------------- /test/bundler-to-file/index.webc: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/custom-js-front-matter/page.liquid: -------------------------------------------------------------------------------- 1 | ---js 2 | const frontmatterdata = "HELLO FROM FRONT MATTER"; 3 | --- 4 | {{ frontmatterdata }} -------------------------------------------------------------------------------- /test/components-in-layouts/_includes/layout.webc: -------------------------------------------------------------------------------- 1 | --- 2 | layout: base.webc 3 | --- 4 | 5 | -------------------------------------------------------------------------------- /test/nested-layouts/_components/say-hello.webc: -------------------------------------------------------------------------------- 1 | Using raw here to test reprocessing in the layout 2 | -------------------------------------------------------------------------------- /test/nested-layouts/_includes/layout.webc: -------------------------------------------------------------------------------- 1 | --- 2 | layout: base.webc 3 | --- 4 | Base 5 | 6 | -------------------------------------------------------------------------------- /test/raw-layout-html/_includes/say-hello.webc: -------------------------------------------------------------------------------- 1 | Using raw here to test reprocessing in the layout 2 | -------------------------------------------------------------------------------- /test/uid-leftovers/_layouts/layout.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/generic.eleventy.config.js: -------------------------------------------------------------------------------- 1 | const EleventyWebcPlugin = require("../eleventyWebcPlugin.js"); 2 | 3 | module.exports = function (eleventyConfig) { 4 | eleventyConfig.addPlugin(EleventyWebcPlugin); 5 | } 6 | -------------------------------------------------------------------------------- /test/sample-transform/page.liquid: -------------------------------------------------------------------------------- 1 | --- 2 | frontmatterdata: "HELLO FROM FRONT MATTER" 3 | --- 4 | 5 | 6 | WHO IS THIS 7 | hi 8 | {{ frontmatterdata }} -------------------------------------------------------------------------------- /test/default-components/page.webc: -------------------------------------------------------------------------------- 1 | --- 2 | frontmatterdata: "HELLO FROM FRONT MATTER" 3 | --- 4 | 5 | 6 | WHO IS THIS 7 | hi 8 | -------------------------------------------------------------------------------- /test/bundler-helpers/eleventy.config.js: -------------------------------------------------------------------------------- 1 | const EleventyWebcPlugin = require("../../eleventyWebcPlugin.js"); 2 | 3 | module.exports = function (eleventyConfig) { 4 | eleventyConfig.addPlugin(EleventyWebcPlugin); 5 | } 6 | -------------------------------------------------------------------------------- /test/custom-permalink/eleventy.config.js: -------------------------------------------------------------------------------- 1 | const EleventyWebcPlugin = require("../../eleventyWebcPlugin.js"); 2 | 3 | module.exports = function (eleventyConfig) { 4 | eleventyConfig.addPlugin(EleventyWebcPlugin); 5 | } 6 | -------------------------------------------------------------------------------- /test/permalink-string/eleventy.config.js: -------------------------------------------------------------------------------- 1 | const EleventyWebcPlugin = require("../../eleventyWebcPlugin.js"); 2 | 3 | module.exports = function (eleventyConfig) { 4 | eleventyConfig.addPlugin(EleventyWebcPlugin); 5 | } 6 | -------------------------------------------------------------------------------- /test/default-components/eleventy.config.js: -------------------------------------------------------------------------------- 1 | const EleventyWebcPlugin = require("../../eleventyWebcPlugin.js"); 2 | 3 | module.exports = function (eleventyConfig) { 4 | eleventyConfig.addPlugin(EleventyWebcPlugin); 5 | } 6 | -------------------------------------------------------------------------------- /test/sample-1/page.webc: -------------------------------------------------------------------------------- 1 | --- 2 | frontmatterdata: "HELLO FROM FRONT MATTER" 3 | layout: layout.webc 4 | --- 5 | 6 | 7 | WHO IS THIS 8 | hi 9 | -------------------------------------------------------------------------------- /test/sample-2/page.webc: -------------------------------------------------------------------------------- 1 | --- 2 | frontmatterdata: "HELLO FROM FRONT MATTER" 3 | layout: layout.webc 4 | --- 5 | 6 | 7 | WHO IS THIS 8 | hi 9 | -------------------------------------------------------------------------------- /test/sample-universal-helpers/page.webc: -------------------------------------------------------------------------------- 1 | --- 2 | globalData: hello 3 | --- 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /test/raw-layout-html/page.webc: -------------------------------------------------------------------------------- 1 | --- 2 | frontmatterdata: "HELLO FROM FRONT MATTER" 3 | layout: layout.webc 4 | --- 5 | 6 | 7 | WHO IS THIS 8 | hi 9 | -------------------------------------------------------------------------------- /test/shortcodes-issue-16/page.webc: -------------------------------------------------------------------------------- 1 | --- 2 | frontmatterdata: "HELLO FROM FRONT MATTER" 3 | --- 4 | -------------------------------------------------------------------------------- /test/sample-non-webc-layout/page.webc: -------------------------------------------------------------------------------- 1 | --- 2 | frontmatterdata: "HELLO FROM FRONT MATTER" 3 | layout: layout.liquid 4 | --- 5 | 6 | 7 | WHO IS THIS 8 | hi 9 | -------------------------------------------------------------------------------- /test/custom-js-front-matter/eleventy.config.js: -------------------------------------------------------------------------------- 1 | const EleventyWebcPlugin = require("../../eleventyWebcPlugin.js"); 2 | 3 | module.exports = function (eleventyConfig) { 4 | eleventyConfig.addPlugin(EleventyWebcPlugin, { 5 | components: false, 6 | }); 7 | } 8 | -------------------------------------------------------------------------------- /test/script-and-style-buckets/page.webc: -------------------------------------------------------------------------------- 1 | --- 2 | layout: layout.webc 3 | --- 4 | 5 |

Hello everyone!

6 | Buttons are purple 7 | 8 | -------------------------------------------------------------------------------- /test/webc-component-in-layout/_components/twitter-share.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /test/custom-permalink/page.webc: -------------------------------------------------------------------------------- 1 | --- 2 | frontmatterdata: "HELLO FROM FRONT MATTER" 3 | permalink: "slugify(frontmatterdata) + '.html'" 4 | --- 5 | 6 | 7 | WHO IS THIS 8 | hi 9 | -------------------------------------------------------------------------------- /test/bundler-helpers/index.webc: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /test/script-and-style-buckets/_components/my-button.webc: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /test/webc-component-in-layout/_includes/layout.liquid: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {% renderTemplate "webc" %}{% endrenderTemplate %} 7 | {{ content }} 8 | 9 | -------------------------------------------------------------------------------- /test/sample-1/eleventy.config.js: -------------------------------------------------------------------------------- 1 | const EleventyWebcPlugin = require("../../eleventyWebcPlugin.js"); 2 | 3 | module.exports = function (eleventyConfig) { 4 | eleventyConfig.addPlugin(EleventyWebcPlugin); 5 | 6 | return { 7 | dir: { 8 | includes: "_includes", 9 | layouts: "_layouts", 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /test/custom-permalink-issue-32/eleventy.config.js: -------------------------------------------------------------------------------- 1 | const EleventyWebcPlugin = require("../../eleventyWebcPlugin.js"); 2 | 3 | module.exports = function (eleventyConfig) { 4 | eleventyConfig.addGlobalData("permalink", () => ((data) => `${data.page.filePathStem}.html`)); 5 | 6 | eleventyConfig.addPlugin(EleventyWebcPlugin); 7 | } 8 | -------------------------------------------------------------------------------- /test/sample-page-global-components/eleventy.config.js: -------------------------------------------------------------------------------- 1 | const EleventyWebcPlugin = require("../../eleventyWebcPlugin.js"); 2 | 3 | module.exports = function (eleventyConfig) { 4 | eleventyConfig.addPlugin(EleventyWebcPlugin); 5 | 6 | return { 7 | dir: { 8 | includes: "_includes", 9 | layouts: "_layouts", 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /test/sample-page-global-components-relative-to-inputpath/page.webc: -------------------------------------------------------------------------------- 1 | --- 2 | frontmatterdata: "HELLO FROM FRONT MATTER" 3 | layout: layout.webc 4 | webc: 5 | components: "./_includes/page1-components/*.webc" 6 | --- 7 | 8 | 9 | WHO IS THIS 10 | hi 11 | -------------------------------------------------------------------------------- /test/sample-permalink-false-webc/page.webc: -------------------------------------------------------------------------------- 1 | --- 2 | permalink: false 3 | --- 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /test/sample-page-global-components/page.webc: -------------------------------------------------------------------------------- 1 | --- 2 | frontmatterdata: "HELLO FROM FRONT MATTER" 3 | layout: layout.webc 4 | webc: 5 | components: "~/test/sample-page-global-components/_includes/page1-components/*.webc" 6 | --- 7 | 8 | 9 | WHO IS THIS 10 | hi 11 | -------------------------------------------------------------------------------- /test/sample-permalink-false/page.liquid: -------------------------------------------------------------------------------- 1 | --- 2 | permalink: false 3 | --- 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/custom-permalink-not-dynamic-issue-32/eleventy.config.js: -------------------------------------------------------------------------------- 1 | const EleventyWebcPlugin = require("../../eleventyWebcPlugin.js"); 2 | 3 | module.exports = function (eleventyConfig) { 4 | eleventyConfig.addGlobalData("dynamicPermalink", false); 5 | eleventyConfig.addGlobalData("permalink", "page.html"); 6 | 7 | eleventyConfig.addPlugin(EleventyWebcPlugin); 8 | } 9 | -------------------------------------------------------------------------------- /test/render-plugin/eleventy.config.mjs: -------------------------------------------------------------------------------- 1 | import WebcPlugin from "../../eleventyWebcPlugin.js"; 2 | import { RenderPlugin } from "@11ty/eleventy"; 3 | 4 | export default function (eleventyConfig) { 5 | eleventyConfig.addPlugin(WebcPlugin, { 6 | components: "./test/render-plugin/webc/*.webc" 7 | }); 8 | 9 | eleventyConfig.addPlugin(RenderPlugin); 10 | } 11 | -------------------------------------------------------------------------------- /test/sample-page-global-components-relative-to-inputpath/eleventy.config.js: -------------------------------------------------------------------------------- 1 | const EleventyWebcPlugin = require("../../eleventyWebcPlugin.js"); 2 | 3 | module.exports = function (eleventyConfig) { 4 | eleventyConfig.addPlugin(EleventyWebcPlugin); 5 | 6 | return { 7 | dir: { 8 | includes: "_includes", 9 | layouts: "_layouts", 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | indent_size = 2 6 | end_of_line = lf 7 | insert_final_newline = false 8 | trim_trailing_whitespace = true 9 | charset = utf-8 10 | 11 | [*.js] 12 | insert_final_newline = true 13 | 14 | [test/*.js] 15 | trim_trailing_whitespace = false 16 | 17 | [test/*.mjs] 18 | trim_trailing_whitespace = false 19 | -------------------------------------------------------------------------------- /test/nested-layouts/eleventy.config.js: -------------------------------------------------------------------------------- 1 | const EleventyWebcPlugin = require("../../eleventyWebcPlugin.js"); 2 | 3 | module.exports = function (eleventyConfig) { 4 | eleventyConfig.addPlugin(EleventyWebcPlugin, { 5 | components: "test/nested-layouts/_components/**/*.webc" 6 | }); 7 | 8 | return { 9 | dir: { 10 | includes: "_includes", 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/sample-permalink-false/eleventy.config.js: -------------------------------------------------------------------------------- 1 | const EleventyWebcPlugin = require("../../eleventyWebcPlugin.js"); 2 | 3 | module.exports = function (eleventyConfig) { 4 | eleventyConfig.addPlugin(EleventyWebcPlugin, { 5 | useTransform: true 6 | }); 7 | 8 | return { 9 | dir: { 10 | includes: "_includes", 11 | layouts: "_layouts", 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/sample-permalink-false-webc/eleventy.config.js: -------------------------------------------------------------------------------- 1 | const EleventyWebcPlugin = require("../../eleventyWebcPlugin.js"); 2 | 3 | module.exports = function (eleventyConfig) { 4 | eleventyConfig.addPlugin(EleventyWebcPlugin, { 5 | useTransform: true 6 | }); 7 | 8 | return { 9 | dir: { 10 | includes: "_includes", 11 | layouts: "_layouts", 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/components-in-layouts/eleventy.config.js: -------------------------------------------------------------------------------- 1 | const EleventyWebcPlugin = require("../../eleventyWebcPlugin.js"); 2 | 3 | module.exports = function (eleventyConfig) { 4 | eleventyConfig.addPlugin(EleventyWebcPlugin, { 5 | components: "test/components-in-layouts/_components/**/*.webc" 6 | }); 7 | 8 | return { 9 | dir: { 10 | includes: "_includes", 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/raw-layout-html/eleventy.config.js: -------------------------------------------------------------------------------- 1 | const EleventyWebcPlugin = require("../../eleventyWebcPlugin.js"); 2 | 3 | module.exports = function (eleventyConfig) { 4 | eleventyConfig.addPlugin(EleventyWebcPlugin, { 5 | components: "test/raw-layout-html/_includes/*.webc" 6 | }); 7 | 8 | return { 9 | dir: { 10 | includes: "_includes", 11 | layouts: "_layouts", 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/sample-non-webc-layout/eleventy.config.js: -------------------------------------------------------------------------------- 1 | const EleventyWebcPlugin = require("../../eleventyWebcPlugin.js"); 2 | 3 | module.exports = function (eleventyConfig) { 4 | eleventyConfig.addPlugin(EleventyWebcPlugin, { 5 | components: "./test/sample-non-webc-layout/_includes/*.webc" 6 | }); 7 | 8 | return { 9 | dir: { 10 | includes: "_includes", 11 | layouts: "_layouts", 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/nested-layouts/_includes/base.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /test/sample-html-preprocess/eleventy.config.js: -------------------------------------------------------------------------------- 1 | const EleventyWebcPlugin = require("../../eleventyWebcPlugin.js"); 2 | 3 | module.exports = function (eleventyConfig) { 4 | eleventyConfig.addPlugin(EleventyWebcPlugin, { 5 | components: "./test/sample-html-preprocess/_includes/*.webc", 6 | }); 7 | 8 | return { 9 | dir: { 10 | includes: "_includes", 11 | }, 12 | htmlTemplateEngine: "webc" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/sample-2/eleventy.config.js: -------------------------------------------------------------------------------- 1 | const EleventyWebcPlugin = require("../../eleventyWebcPlugin.js"); 2 | 3 | module.exports = function (eleventyConfig) { 4 | eleventyConfig.addPlugin(EleventyWebcPlugin, { 5 | components: "test/sample-2/_includes/**/*.webc" 6 | }); 7 | // eleventyConfig.addPlugin(EleventyWebcPlugin); 8 | 9 | return { 10 | dir: { 11 | includes: "_includes", 12 | layouts: "_layouts", 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /test/components-in-layouts/_includes/base.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/sample-transform/eleventy.config.js: -------------------------------------------------------------------------------- 1 | const EleventyWebcPlugin = require("../../eleventyWebcPlugin.js"); 2 | 3 | module.exports = function (eleventyConfig) { 4 | eleventyConfig.addPlugin(EleventyWebcPlugin, { 5 | components: "./test/sample-transform/_includes/*.webc", 6 | useTransform: true, 7 | transformData: { pkg: require("../../package.json") } 8 | }); 9 | 10 | return { 11 | dir: { 12 | includes: "_includes", 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /test/sample-universal-helpers/eleventy.config.js: -------------------------------------------------------------------------------- 1 | const EleventyWebcPlugin = require("../../eleventyWebcPlugin.js"); 2 | 3 | module.exports = function (eleventyConfig) { 4 | eleventyConfig.addPlugin(EleventyWebcPlugin); 5 | eleventyConfig.addFilter("testing", function(arg) { 6 | return arg + "Always return this" + this.page.url 7 | }); 8 | 9 | return { 10 | dir: { 11 | includes: "_includes", 12 | layouts: "_layouts", 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/raw-layout-html/_layouts/layout.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/webc-component-in-layout/eleventy.config.mjs: -------------------------------------------------------------------------------- 1 | import { RenderPlugin } from "@11ty/eleventy"; 2 | import WebcPlugin from "../../eleventyWebcPlugin.js"; 3 | 4 | export default function(eleventyConfig) { 5 | eleventyConfig.addPlugin(RenderPlugin); 6 | 7 | eleventyConfig.addPlugin(WebcPlugin, { 8 | components: "./test/webc-component-in-layout/_components/*.webc" 9 | }); 10 | 11 | return { 12 | dir: { 13 | includes: "_includes", 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/uid-leftovers/eleventy.config.js: -------------------------------------------------------------------------------- 1 | const EleventyWebcPlugin = require("../../eleventyWebcPlugin.js"); 2 | 3 | let i = 0; 4 | 5 | module.exports = function (eleventyConfig) { 6 | eleventyConfig.addPlugin(EleventyWebcPlugin, { 7 | before: (component) => { 8 | component.setUidFunction(function() { 9 | return `webc-${i++}`; 10 | }); 11 | } 12 | }); 13 | 14 | return { 15 | dir: { 16 | includes: "_includes", 17 | layouts: "_layouts", 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/sample-non-webc-layout/_layouts/layout.liquid: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | {{ content }} 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/shortcodes-issue-16/eleventy.config.js: -------------------------------------------------------------------------------- 1 | const EleventyWebcPlugin = require("../../eleventyWebcPlugin.js"); 2 | 3 | module.exports = function (eleventyConfig) { 4 | eleventyConfig.addShortcode("testing", () => { 5 | // WebC in a shortcode! 6 | return ""; 7 | }); 8 | 9 | eleventyConfig.addFilter("uppercase", (str) => { 10 | return str.toUpperCase(); 11 | }); 12 | 13 | 14 | eleventyConfig.addPlugin(EleventyWebcPlugin, { 15 | components: "./test/shortcodes-issue-16/_components/*.webc" 16 | }); 17 | }; 18 | -------------------------------------------------------------------------------- /test/sample-1/_layouts/layout.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/script-and-style-buckets/eleventy.config.mjs: -------------------------------------------------------------------------------- 1 | import { RenderPlugin } from "@11ty/eleventy"; 2 | import WebcPlugin from "../../eleventyWebcPlugin.js"; 3 | 4 | export default function (eleventyConfig) { 5 | eleventyConfig.addPlugin(RenderPlugin); 6 | eleventyConfig.addPlugin(WebcPlugin, { 7 | components: "test/script-and-style-buckets/_components/**/*.webc" 8 | }); 9 | // eleventyConfig.addPlugin(eleventyConfig => { 10 | // console.log( eleventyConfig.javascriptFunctions ); 11 | // }) 12 | 13 | return { 14 | dir: { 15 | includes: "_includes", 16 | layouts: "_layouts", 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/script-and-style-buckets/_layouts/layout.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/sample-2/_layouts/layout.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/sample-page-global-components/_layouts/layout.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/sample-page-global-components-relative-to-inputpath/_layouts/layout.webc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Node Unit Tests 2 | on: push 3 | permissions: read-all 4 | jobs: 5 | build: 6 | runs-on: ${{ matrix.os }} 7 | strategy: 8 | matrix: 9 | os: ["ubuntu-latest", "macos-latest", "windows-latest"] 10 | node: ["18", "20", "22", "24"] 11 | name: Node.js ${{ matrix.node }} on ${{ matrix.os }} 12 | steps: 13 | - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # 4.1.7 14 | - name: Setup node 15 | uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # 4.0.3 16 | with: 17 | node-version: ${{ matrix.node }} 18 | # cache: npm 19 | - run: npm install 20 | - run: npm test 21 | env: 22 | YARN_GPG: no 23 | -------------------------------------------------------------------------------- /test/test-chdir.mjs: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import Eleventy from "@11ty/eleventy"; 3 | 4 | function normalize(str) { 5 | return str.trim().replace(/\r\n/g, "\n"); 6 | } 7 | 8 | // This needs a CHDIR because the default `components` glob is relative to the root directory (not the input directory) 9 | console.log("Make sure you are running `npm run test` and not `npx ava`"); 10 | process.chdir("./test/default-components/"); 11 | 12 | test("New default components directory, issue #14", async t => { 13 | let elev = new Eleventy("page.webc", "_site", { 14 | configPath: "eleventy.config.js" 15 | }); 16 | 17 | let results = await elev.toJSON(); 18 | let [result] = results; 19 | t.is(normalize(result.content), `COMPONENTS DIR 20 | COMPONENTS DIR 21 | WHO IS THIS 22 | hi 23 | HELLO FROM FRONT MATTER`); 24 | }); 25 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Publish Release to npm 2 | on: 3 | release: 4 | types: [published] 5 | permissions: read-all 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | environment: GitHub Publish 10 | permissions: 11 | contents: read 12 | id-token: write 13 | steps: 14 | - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # 4.1.7 15 | - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # 4.0.3 16 | with: 17 | node-version: "22" 18 | registry-url: 'https://registry.npmjs.org' 19 | - run: npm install -g npm@latest 20 | - run: npm ci 21 | - run: npm test 22 | - if: ${{ github.event.release.tag_name != '' && env.NPM_PUBLISH_TAG != '' }} 23 | run: npm publish --provenance --access=public --tag=${{ env.NPM_PUBLISH_TAG }} 24 | env: 25 | NPM_PUBLISH_TAG: ${{ contains(github.event.release.tag_name, '-beta.') && 'beta' || 'latest' }} 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@11ty/eleventy-plugin-webc", 3 | "version": "0.12.0-beta.7", 4 | "description": "WebC support for Eleventy adds for Single File Web Components", 5 | "main": "eleventyWebcPlugin.js", 6 | "scripts": { 7 | "test": "npx ava --no-worker-threads" 8 | }, 9 | "publishConfig": { 10 | "access": "public" 11 | }, 12 | "license": "MIT", 13 | "engines": { 14 | "node": ">= 18" 15 | }, 16 | "11ty": { 17 | "compatibility": ">=3.0.0" 18 | }, 19 | "funding": { 20 | "type": "opencollective", 21 | "url": "https://opencollective.com/11ty" 22 | }, 23 | "keywords": [ 24 | "eleventy", 25 | "eleventy-plugin", 26 | "web-components", 27 | "custom-elements" 28 | ], 29 | "author": "Zach Leatherman (https://zachleat.com/)", 30 | "repository": { 31 | "type": "git", 32 | "url": "git://github.com/11ty/eleventy-plugin-webc.git" 33 | }, 34 | "bugs": "https://github.com/11ty/eleventy-plugin-webc/issues", 35 | "homepage": "https://github.com/11ty/eleventy-plugin-webc", 36 | "ava": { 37 | "failFast": true, 38 | "files": [ 39 | "./test/test*.js", 40 | "./test/test*.mjs" 41 | ], 42 | "watchMode": { 43 | "ignoreChanges": [ 44 | "**/_site/**", 45 | ".cache" 46 | ] 47 | } 48 | }, 49 | "dependencies": { 50 | "@11ty/eleventy-plugin-bundle": "^3.0.7", 51 | "@11ty/webc": "^0.12.0-beta.2", 52 | "import-module-string": "^2.0.3" 53 | }, 54 | "devDependencies": { 55 | "@11ty/eleventy": "^3.1.2", 56 | "ava": "^6.4.1" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/eleventyWebcTransform.js: -------------------------------------------------------------------------------- 1 | module.exports = function(eleventyConfig, options = {}) { 2 | let componentsMap = false; // cache the glob search 3 | 4 | eleventyConfig.on("eleventy.before", () => { 5 | componentsMap = false; 6 | }); 7 | 8 | let scopedHelpers = new Set(options.scopedHelpers); 9 | 10 | eleventyConfig.addTransform("@11ty/eleventy-plugin-webc", async function(content) { 11 | // Skip non-.html output 12 | // Skip .webc input 13 | if(this.inputPath.endsWith(".webc") || !(this.outputPath || "").endsWith(".html")) { 14 | return content; 15 | } 16 | 17 | // TODO prevent double processing with the Render plugin with WebC 18 | // https://www.11ty.dev/docs/plugins/render/ 19 | try { 20 | const { WebC } = await import("@11ty/webc"); 21 | let page = new WebC(); 22 | 23 | if(componentsMap === false && options.components) { 24 | componentsMap = WebC.getComponentsMap(options.components); // "./_components/**/*.webc" 25 | } 26 | 27 | for(let helperName in eleventyConfig.javascriptFunctions) { 28 | page.setHelper(helperName, eleventyConfig.javascriptFunctions[helperName], scopedHelpers.has(helperName)); 29 | } 30 | 31 | page.setBundlerMode(false); 32 | page.defineComponents(componentsMap); 33 | page.setContent(content, this.outputPath); 34 | 35 | let { html } = await page.compile({ 36 | // global data 37 | data: options.transformData 38 | }); 39 | return html; 40 | } catch(e) { 41 | console.error( `[11ty/eleventy-plugin-webc] Error transforming ${this.inputPath}`, e ); 42 | throw e; 43 | } 44 | 45 | return content; 46 | }); 47 | } 48 | -------------------------------------------------------------------------------- /src/dynamicScript.js: -------------------------------------------------------------------------------- 1 | // TODO move this upstream into import-module-string 2 | // Code cribbed from WebC’s attributeSerializer.js 3 | async function evaluateInlineCode(codeString, options = {}) { 4 | let { filePath, context, data } = Object.assign({ data: {} }, options); 5 | 6 | let { parseCode, walkCode, importFromString } = await import("import-module-string"); 7 | let varsInUse = []; 8 | try { 9 | let {used} = walkCode(parseCode(codeString)); 10 | varsInUse = used; 11 | } catch(e) { 12 | let errorString = `Error parsing inline code: \`${codeString}\``; 13 | 14 | // Issue #45: very defensive error message here. We only throw this error when an error is thrown during compilation. 15 | if(e.message.startsWith("Unexpected token ") && codeString.match(/\bclass\b/) && !codeString.match(/\bclass\b\s*\{/)) { 16 | throw new Error(`${errorString ? `${errorString} ` : ""}\`class\` is a reserved word in JavaScript. Change \`class\` to \`this.class\` instead!`); 17 | } 18 | 19 | throw new Error(`${errorString}\nOriginal error message: ${e.message}`); 20 | } 21 | 22 | let argString = ""; 23 | if(!Array.isArray(varsInUse)) { 24 | varsInUse = Array.from(varsInUse) 25 | } 26 | if(varsInUse.length > 0) { 27 | argString = `{ ${varsInUse.join(", ")} }`; 28 | } 29 | 30 | let code = `export default function(${argString}) { return ${codeString} };`; 31 | 32 | let evaluated = await importFromString(code, { 33 | filePath, 34 | implicitExports: false, 35 | }).then(mod => { 36 | let fn = mod.default; 37 | if(context) { 38 | return fn.call(context, data); 39 | } 40 | return fn(data); 41 | }).catch(e => { 42 | throw new Error(`Error evaluating inline code: \`${codeString}\``, { cause: e }); 43 | }); 44 | 45 | return evaluated; 46 | } 47 | 48 | module.exports = { 49 | evaluateInlineCode 50 | }; 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

11ty Logo

2 | 3 | # eleventy-plugin-webc 🕚⚡️🎈🐀 4 | 5 | Adds support for [WebC, the single file web component format](https://github.com/11ty/webc), to Eleventy. 6 | 7 | * [This documentation has moved to 11ty.dev](https://www.11ty.dev/docs/languages/webc/). 8 | * Watch the [crash course in Eleventy WebC on YouTube](https://www.youtube.com/watch?v=X-Bpjrkz-V8). 9 | * Watch the [Interactive Components tutorial on YouTube](https://www.youtube.com/watch?v=p0wDUK0Z5Nw) 10 | 11 | [![npm Version](https://img.shields.io/npm/v/@11ty/eleventy-plugin-webc.svg?style=for-the-badge)](https://www.npmjs.com/package/@11ty/eleventy-plugin-webc) 12 | 13 | - Star [Eleventy on GitHub](https://github.com/11ty/eleventy/)! 14 | - Follow us on Twitter [@eleven_ty](https://twitter.com/eleven_ty) 15 | - Support [11ty on Open Collective](https://opencollective.com/11ty) 16 | - Subscribe to our [YouTube channel](https://11ty.dev/youtube) 17 | 18 | ## [Documentation](https://www.11ty.dev/docs/languages/webc/) 19 | 20 | This documentation has [moved to 11ty.dev](https://www.11ty.dev/docs/languages/webc/). 21 | 22 | ## Features 23 | 24 | * Brings first-class **components** to Eleventy. 25 | * Expand any HTML element (including custom elements) to HTML with defined conventions from web standards. 26 | * This means that Web Components created with WebC are compatible with server-side rendering (without duplicating author-written markup) 27 | * WebC components are [Progressive Enhancement friendly](https://www.youtube.com/watch?v=p0wDUK0Z5Nw). 28 | * Get first-class **incremental builds** (for page templates, components, and Eleventy layouts) when [used with `--incremental`](https://www.11ty.dev/docs/usage/#incremental-for-partial-incremental-builds) 29 | * Streaming friendly (stream on the Edge 👀) 30 | * Easily scope component CSS (or use your own scoping utility). 31 | * Tired of importing components? Use global or per-page no-import components. 32 | * Shadow DOM friendly (works with or without Shadow DOM) 33 | * All configuration extensions/hooks into WebC are async-friendly out of the box. 34 | * Bundler mode: Easily roll up the CSS and JS in-use by WebC components on a page for page-specific bundles. Dirt-simple critical CSS/JS to only load the code you need. 35 | * For more complex templating needs, render any existing Eleventy template syntax (Liquid, markdown, Nunjucks, etc.) inside of WebC. 36 | * Works great with [is-land](https://www.11ty.dev/docs/plugins/partial-hydration/) for web component hydration. 37 | -------------------------------------------------------------------------------- /eleventyWebcPlugin.js: -------------------------------------------------------------------------------- 1 | const pkg = require("./package.json"); 2 | const templatePlugin = require("./src/eleventyWebcTemplate.js"); 3 | const transformPlugin = require("./src/eleventyWebcTransform.js"); 4 | 5 | module.exports = function(eleventyConfig, options = {}) { 6 | eleventyConfig.versionCheck(pkg["11ty"].compatibility); 7 | 8 | // Error for removed filters. 9 | eleventyConfig.addFilter("webcGetCss", () => { 10 | throw new Error("webcGetCss was removed from @11ty/eleventy-plugin-webc. Use the `getBundle('css')` shortcode instead.") 11 | }); 12 | 13 | // Error for removed filters. 14 | eleventyConfig.addFilter("webcGetJs", () => { 15 | throw new Error("webcGetJs was removed from @11ty/eleventy-plugin-webc. Use the `getBundle('js')` shortcode instead.") 16 | }) 17 | 18 | 19 | options = Object.assign({ 20 | components: "_components/**/*.webc", // glob for no-import global components 21 | scopedHelpers: ["css", "js", "html"], 22 | useTransform: false, // global transform 23 | transformData: {}, // extra global data for transforms specifically 24 | }, options); 25 | 26 | options.bundlePluginOptions = Object.assign({ 27 | hoistDuplicateBundlesFor: ["css", "js"] 28 | }, options.bundlePluginOptions); 29 | 30 | if(options.components) { 31 | let components = options.components; 32 | if(!Array.isArray(components)) { 33 | components = [components]; 34 | } 35 | 36 | for(let entry of components) { 37 | if(entry.startsWith("npm:")) { 38 | continue; 39 | } 40 | 41 | eleventyConfig.addWatchTarget(entry); 42 | 43 | // Opt-out of Eleventy to process components 44 | // Note that Eleventy’s default ignores already have _includes/** 45 | 46 | // This will cause component files outside of _includes to not be watched: https://github.com/11ty/eleventy-plugin-webc/issues/29 47 | // Fixed in @11ty/eleventy@2.0.0-canary.18: https://github.com/11ty/eleventy/issues/893 48 | eleventyConfig.ignores.add(entry); 49 | } 50 | } 51 | 52 | // v0.12.0 upstream 53 | // `bundlePluginOptions.toFileDirectory` (via Bundle Plugin changes) default changed from "bundle" to "" 54 | // https://github.com/11ty/eleventy-plugin-bundle/releases/tag/v2.0.0 55 | 56 | eleventyConfig.addBundle("html", Object.assign({}, options.bundlePluginOptions, { 57 | hoist: options.bundlePluginOptions.hoistDuplicateBundlesFor.includes("html"), 58 | })); 59 | eleventyConfig.addBundle("css", Object.assign({}, options.bundlePluginOptions, { 60 | hoist: options.bundlePluginOptions.hoistDuplicateBundlesFor.includes("css"), 61 | })); 62 | eleventyConfig.addBundle("js", Object.assign({}, options.bundlePluginOptions, { 63 | hoist: options.bundlePluginOptions.hoistDuplicateBundlesFor.includes("js") 64 | })); 65 | 66 | templatePlugin(eleventyConfig, options); 67 | 68 | if(options.useTransform) { 69 | transformPlugin(eleventyConfig, options); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/eleventyWebcTemplate.js: -------------------------------------------------------------------------------- 1 | const path = require("node:path"); 2 | const debug = require("debug")("Eleventy:WebC"); 3 | 4 | const { evaluateInlineCode } = require("./dynamicScript.js"); 5 | 6 | function relativePath(inputPath, newGlob) { 7 | // project root 8 | if(newGlob.startsWith("~/")) { 9 | let rootRelativePath = "." + newGlob.slice(1); 10 | return rootRelativePath; 11 | } 12 | 13 | let { dir } = path.parse(inputPath); 14 | // globs must have forward slashes (even on Windows) 15 | let templateRelativePath = path.join(dir, newGlob).split(path.sep).join("/"); 16 | return templateRelativePath; 17 | } 18 | 19 | function addContextToJavaScriptFunction(data, fn) { 20 | let CONTEXT_KEYS = ["eleventy", "page"]; 21 | return function (...args) { 22 | for (let key of CONTEXT_KEYS) { 23 | if (data && data[key]) { 24 | this[key] = data[key]; 25 | } 26 | } 27 | 28 | return fn.call(this, ...args); 29 | }; 30 | } 31 | 32 | module.exports = function(eleventyConfig, options = {}) { 33 | // TODO remove this when WebC is moved out of plugin-land into core. 34 | eleventyConfig.addTemplateFormats("webc"); 35 | 36 | let _WebC; 37 | let globalComponentManager; 38 | let componentsMap = false; // cache the glob search 39 | let scopedHelpers = new Set(options.scopedHelpers); 40 | 41 | eleventyConfig.on("eleventy.before", async () => { 42 | // Temporary workaround for ESM in CJS 43 | let { WebC, ComponentManager } = await import("@11ty/webc"); 44 | _WebC = WebC; 45 | globalComponentManager = new ComponentManager(); 46 | 47 | if(options.components) { 48 | componentsMap = WebC.getComponentsMap(options.components); // second argument is ignores here 49 | } 50 | }); 51 | 52 | let templateConfig; 53 | eleventyConfig.on("eleventy.config", (cfg) => { 54 | templateConfig = cfg; 55 | }); 56 | 57 | eleventyConfig.addExtension("webc", { 58 | outputFileExtension: "html", 59 | 60 | compileOptions: { 61 | permalink: function(contents, inputPath) { 62 | if(contents && typeof contents === "string") { 63 | return async (data) => { 64 | let combinedContextData = { 65 | ...this, 66 | ...data, 67 | }; 68 | 69 | // Hard to know if this is JavaScript code or just a raw string value. 70 | return evaluateInlineCode(contents, { 71 | filePath: inputPath, 72 | context: combinedContextData, 73 | data: combinedContextData, 74 | }).catch(e => { 75 | debug("Error evaluating dynamic permalink, returning raw string contents instead: %o\n%O", contents, e); 76 | return contents; 77 | }); 78 | } 79 | } 80 | 81 | return contents; 82 | } 83 | }, 84 | 85 | compile: async function(inputContent, inputPath) { 86 | let page = new _WebC(); 87 | 88 | page.setGlobalComponentManager(globalComponentManager); 89 | page.setBundlerMode(true); 90 | page.setContent(inputContent, inputPath); 91 | 92 | if(componentsMap) { 93 | page.defineComponents(componentsMap); 94 | } 95 | 96 | // Support both casings (I prefer getCss, but yeah) 97 | page.setHelper("getCss", (url, bucket) => this.config.javascriptFunctions.getBundle("css", bucket, url), scopedHelpers.has("getCss")); 98 | page.setHelper("getCSS", (url, bucket) => this.config.javascriptFunctions.getBundle("css", bucket, url), scopedHelpers.has("getCSS")); 99 | 100 | page.setHelper("getJs", (url, bucket) => this.config.javascriptFunctions.getBundle("js", bucket, url), scopedHelpers.has("getJs")); 101 | page.setHelper("getJS", (url, bucket) => this.config.javascriptFunctions.getBundle("js", bucket, url), scopedHelpers.has("getJS")); 102 | 103 | page.setTransform("11ty", async function(content) { 104 | let syntax = this["11ty:type"]; 105 | if(syntax) { 106 | const { RenderPlugin } = await import("@11ty/eleventy"); 107 | const CompileString = RenderPlugin.String; 108 | 109 | let fn = await CompileString(content, syntax, { 110 | templateConfig 111 | }); 112 | return fn(this); 113 | } 114 | return content; 115 | }); 116 | 117 | // Render function 118 | return async (data) => { 119 | // Add Eleventy JavaScript Functions as WebC helpers 120 | // Note that Universal Filters and Shortcodes populate into javascriptFunctions and will be present here 121 | 122 | for(let helperName in this.config.javascriptFunctions) { 123 | let helperFunction = addContextToJavaScriptFunction(data, this.config.javascriptFunctions[helperName]); 124 | page.setHelper(helperName, helperFunction, scopedHelpers.has(helperName)); 125 | } 126 | 127 | let setupObject = { 128 | data, 129 | }; 130 | 131 | if(data.webc?.components) { 132 | setupObject.components = _WebC.getComponentsMap(relativePath(data.page.inputPath, data.webc.components)); 133 | } 134 | 135 | if(options.before && typeof options.before === "function") { 136 | await options.before(page); 137 | } 138 | 139 | let { html, css, js, buckets, components } = await page.compile(setupObject); 140 | 141 | // 2.0.0-canary.19+ 142 | this.addDependencies(inputPath, components); 143 | 144 | // Add CSS to bundle 145 | this.config.javascriptFunctions.css(css, "default", data.page.url); 146 | 147 | if(buckets.css) { 148 | for(let bucket in buckets.css) { 149 | this.config.javascriptFunctions.css(buckets.css[bucket], bucket, data.page.url); 150 | } 151 | } 152 | 153 | // Add JS to bundle 154 | this.config.javascriptFunctions.js(js, "default", data.page.url); 155 | 156 | if(buckets.js) { 157 | for(let bucket in buckets.js) { 158 | this.config.javascriptFunctions.js(buckets.js[bucket], bucket, data.page.url); 159 | } 160 | } 161 | 162 | return html; 163 | }; 164 | } 165 | }); 166 | }; 167 | -------------------------------------------------------------------------------- /test/test.mjs: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import Eleventy from "@11ty/eleventy"; 3 | 4 | import { createRequire } from "node:module"; 5 | const require = createRequire(import.meta.url); 6 | const pkg = require("../package.json"); 7 | 8 | function normalize(str) { 9 | return str.trim().replace(/\r\n/g, "\n"); 10 | } 11 | 12 | test("Sample page (webc layout)", async t => { 13 | let elev = new Eleventy("./test/sample-1/page.webc", "./test/sample-1/_site", { 14 | configPath: "./test/sample-1/eleventy.config.js" 15 | }); 16 | 17 | let results = await elev.toJSON(); 18 | let [result] = results; 19 | 20 | t.is(normalize(result.content), ` 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | WHO IS THIS 34 | hi 35 | HELLO FROM FRONT MATTER 36 | 37 | 38 | 39 | 40 | 41 | `); 42 | }); 43 | 44 | test("Sample page (liquid layout and one webc component)", async t => { 45 | let elev = new Eleventy("./test/sample-non-webc-layout/page.webc", "./test/sample-non-webc-layout/_site", { 46 | configPath: "./test/sample-non-webc-layout/eleventy.config.js" 47 | }); 48 | 49 | let results = await elev.toJSON(); 50 | let [result] = results; 51 | 52 | t.is(normalize(result.content), ` 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | HELLO 64 | 65 | HELLO 66 | 67 | WHO IS THIS 68 | hi 69 | HELLO FROM FRONT MATTER 70 | 71 | 72 | 73 | 74 | `); 75 | }); 76 | 77 | test("Sample page with global component", async t => { 78 | let elev = new Eleventy("./test/sample-2/page.webc", "./test/sample-2/_site", { 79 | configPath: "./test/sample-2/eleventy.config.js" 80 | }); 81 | 82 | let results = await elev.toJSON(); 83 | let [result] = results; 84 | 85 | t.is(normalize(result.content), ` 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | This is a component. 97 | This is a component. 98 | WHO IS THIS 99 | hi 100 | HELLO FROM FRONT MATTER 101 | 102 | 103 | 104 | 105 | 106 | `); 107 | }); 108 | 109 | test("Page with front matter no-import components", async t => { 110 | let elev = new Eleventy("./test/sample-page-global-components/page.webc", "./test/sample-page-global-components/_site", { 111 | configPath: "./test/sample-page-global-components/eleventy.config.js" 112 | }); 113 | 114 | let results = await elev.toJSON(); 115 | let [result] = results; 116 | 117 | t.is(normalize(result.content), ` 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | HELLO 129 | HELLO 130 | WHO IS THIS 131 | hi 132 | HELLO FROM FRONT MATTER 133 | 134 | 135 | 136 | 137 | 138 | `); 139 | }); 140 | 141 | 142 | test("Page with front matter no-import components (relative to input path)", async t => { 143 | let elev = new Eleventy("./test/sample-page-global-components-relative-to-inputpath/page.webc", "./test/sample-page-global-components-relative-to-inputpath/_site", { 144 | configPath: "./test/sample-page-global-components-relative-to-inputpath/eleventy.config.js" 145 | }); 146 | 147 | let results = await elev.toJSON(); 148 | let [result] = results; 149 | 150 | t.is(normalize(result.content), ` 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | HELLO 162 | HELLO 163 | WHO IS THIS 164 | hi 165 | HELLO FROM FRONT MATTER 166 | 167 | 168 | 169 | 170 | 171 | `); 172 | }); 173 | 174 | test("WebC using a transform", async t => { 175 | let elev = new Eleventy("./test/sample-transform/", "./test/sample-transform/_site", { 176 | configPath: "./test/sample-transform/eleventy.config.js" 177 | }); 178 | 179 | let results = await elev.toJSON(); 180 | let [result] = results; 181 | 182 | t.is(normalize(result.content), `HELLO${pkg.version} 183 | HELLO${pkg.version} 184 | WHO IS THIS 185 | hi 186 | HELLO FROM FRONT MATTER`); 187 | }); 188 | 189 | test("WebC using htmlTemplateEngine", async t => { 190 | let elev = new Eleventy("./test/sample-html-preprocess/", "./test/sample-html-preprocess/_site", { 191 | configPath: "./test/sample-html-preprocess/eleventy.config.js" 192 | }); 193 | 194 | let results = await elev.toJSON(); 195 | let [result] = results; 196 | 197 | t.is(normalize(result.content), `HELLO${pkg.version} 198 | HELLO${pkg.version} 199 | WHO IS THIS 200 | hi`); 201 | }); 202 | 203 | test("Sample page with permalink: false (issue #9)", async t => { 204 | let elev = new Eleventy("./test/sample-permalink-false/", "./test/sample-permalink-false/_site", { 205 | configPath: "./test/sample-permalink-false/eleventy.config.js" 206 | }); 207 | 208 | let results = await elev.toJSON(); 209 | let [result] = results; 210 | 211 | t.is(normalize(result.content), ` 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | `); 223 | }); 224 | 225 | test("Sample WebC page with permalink: false (issue #86)", async t => { 226 | let elev = new Eleventy("./test/sample-permalink-false-webc/", "./test/sample-permalink-false-webc/_site", { 227 | configPath: "./test/sample-permalink-false-webc/eleventy.config.js" 228 | }); 229 | 230 | let results = await elev.toJSON(); 231 | let [result] = results; 232 | 233 | t.is(normalize(result.content), ` 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | `); 244 | }); 245 | 246 | test("Add JS Functions as helpers (universal filters) (issue #3)", async t => { 247 | let elev = new Eleventy("./test/sample-universal-helpers/", "./test/sample-universal-helpers/_site", { 248 | configPath: "./test/sample-universal-helpers/eleventy.config.js" 249 | }); 250 | 251 | let results = await elev.toJSON(); 252 | let [result] = results; 253 | 254 | t.is(normalize(result.content), ` 255 | 256 | 257 | 258 | 259 | helloAlways return this/page/ 260 | `); 261 | }); 262 | 263 | test("Use render plugin #22", async t => { 264 | let elev = new Eleventy("./test/render-plugin/page.md", "./test/render-plugin/_site", { 265 | configPath: "./test/render-plugin/eleventy.config.mjs" 266 | }); 267 | 268 | let results = await elev.toJSON(); 269 | let [result] = results; 270 | 271 | t.is(normalize(result.content), `

Hello

272 |

My component

`); 273 | }); 274 | 275 | test("UID leftovers #17", async t => { 276 | let elev = new Eleventy("./test/uid-leftovers/page.webc", "./test/uid-leftovers/_site", { 277 | configPath: "./test/uid-leftovers/eleventy.config.js" 278 | }); 279 | 280 | await elev.toJSON(); 281 | 282 | let results = await elev.toJSON(); 283 | let [result] = results; 284 | 285 | t.is(normalize(result.content), ` 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | `); 294 | }); 295 | 296 | test("Custom permalink JS, issue #27", async t => { 297 | let elev = new Eleventy("./test/custom-permalink/page.webc", "./test/custom-permalink/_site", { 298 | configPath: "./test/custom-permalink/eleventy.config.js" 299 | }); 300 | 301 | let results = await elev.toJSON(); 302 | let [result] = results; 303 | 304 | t.is(result.url, "/hello-from-front-matter.html") 305 | t.is(normalize(result.content), ` 306 | 307 | WHO IS THIS 308 | hi 309 | HELLO FROM FRONT MATTER`); 310 | }); 311 | 312 | test("Raw layout html to re-enable reprocessing mode in layouts, issue #20", async t => { 313 | let elev = new Eleventy("./test/raw-layout-html/page.webc", "./test/raw-layout-html/_site", { 314 | configPath: "./test/raw-layout-html/eleventy.config.js" 315 | }); 316 | 317 | let results = await elev.toJSON(); 318 | let [result] = results; 319 | t.is(normalize(result.content), ` 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | Using raw here to test reprocessing in the layout 330 | 331 | Using raw here to test reprocessing in the layout 332 | 333 | WHO IS THIS 334 | hi 335 | HELLO FROM FRONT MATTER 336 | REPROCESSED 337 | 338 | 339 | `); 340 | }); 341 | 342 | 343 | test("Shortcodes, issue #16", async t => { 344 | let elev = new Eleventy("./test/shortcodes-issue-16/page.webc", "./test/shortcodes-issue-16/_site", { 345 | configPath: "./test/shortcodes-issue-16/eleventy.config.js" 346 | }); 347 | 348 | let results = await elev.toJSON(); 349 | let [result] = results; 350 | t.is(normalize(result.content), `HELLO FROM FRONT MATTER 351 | COMPONENTS DIR 352 | LOWERCASE`); 353 | }); 354 | 355 | test("Nested layouts", async t => { 356 | let elev = new Eleventy("./test/nested-layouts/page.webc", "./test/nested-layouts/_site", { 357 | configPath: "./test/nested-layouts/eleventy.config.js" 358 | }); 359 | 360 | let results = await elev.toJSON(); 361 | let [result] = results; 362 | t.is(normalize(result.content), ` 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | Base 373 | Testing 374 | 375 | 376 | 377 | 378 | `); 379 | }); 380 | 381 | test("Components in layouts #11", async t => { 382 | let elev = new Eleventy("./test/components-in-layouts/", "./test/components-in-layouts/_site", { 383 | configPath: "./test/components-in-layouts/eleventy.config.js" 384 | }); 385 | 386 | let results = await elev.toJSON(); 387 | let [page1] = results.filter(page => page.inputPath.endsWith("page1.webc")); 388 | let [page2] = results.filter(page => page.inputPath.endsWith("page2.webc")); 389 | let [page3] = results.filter(page => page.inputPath.endsWith("page3.webc")); 390 | 391 | t.is(normalize(page1.content), ` 392 | 393 | 394 | 395 | 396 | 397 | 398 | 400 | 401 | 402 | Page 403 | Test 404 | 405 | Test 406 | 407 | 408 | 409 | `); 410 | 411 | t.is(normalize(page2.content), ` 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | Other page 423 | Test 424 | 425 | Test 426 | 427 | 428 | 429 | `); 430 | 431 | t.is(normalize(page3.content), `No layouts here 432 | 433 | Test 434 | `); 435 | }); 436 | 437 | 438 | test("Helpers in the bundler", async t => { 439 | let elev = new Eleventy("./test/bundler-helpers/index.webc", "./test/bundler-helpers/_site", { 440 | configPath: "./test/bundler-helpers/eleventy.config.js" 441 | }); 442 | 443 | let results = await elev.toJSON(); 444 | let [result] = results; 445 | 446 | t.is(normalize(result.content), ` 448 | `); 450 | }); 451 | 452 | test("page with `javascript` front matter", async t => { 453 | let elev = new Eleventy("./test/custom-js-front-matter/", "./test/custom-js-front-matter/_site", { 454 | configPath: "./test/custom-js-front-matter/eleventy.config.js" 455 | }); 456 | 457 | let results = await elev.toJSON(); 458 | let [result] = results; 459 | 460 | t.is(normalize(result.content), `HELLO FROM FRONT MATTER`); 461 | }); 462 | 463 | test("Custom permalink JS, issue #32", async t => { 464 | let elev = new Eleventy("./test/custom-permalink-issue-32/page.webc", "./test/custom-permalink-issue-32/_site", { 465 | configPath: "./test/custom-permalink-issue-32/eleventy.config.js" 466 | }); 467 | 468 | let results = await elev.toJSON(); 469 | let [result] = results; 470 | 471 | t.is(result.url, "/page.html") 472 | }); 473 | 474 | // waiting on https://github.com/11ty/eleventy/issues/2823 475 | // ref: https://github.com/11ty/eleventy-plugin-webc/issues/32#issuecomment-1440831590 476 | test("Custom permalink JS, `dynamicPermalink: false` issue #32", async t => { 477 | let elev = new Eleventy("./test/custom-permalink-not-dynamic-issue-32/page.webc", "./test/custom-permalink-not-dynamic-issue-32/_site", { 478 | configPath: "./test/custom-permalink-not-dynamic-issue-32/eleventy.config.js" 479 | }); 480 | 481 | let results = await elev.toJSON(); 482 | let [result] = results; 483 | 484 | t.is(result.url, "/page.html") 485 | }); 486 | 487 | test("WebC components in liquid layout, issue #35", async t => { 488 | let elev = new Eleventy("./test/webc-component-in-layout/page.liquid", "./test/webc-component-in-layout/_site", { 489 | configPath: "./test/webc-component-in-layout/eleventy.config.mjs" 490 | }); 491 | 492 | let results = await elev.toJSON(); 493 | let [result] = results; 494 | 495 | t.is(normalize(result.content), ` 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | Hello 506 | 507 | `); 508 | }); 509 | 510 | test("Permalink string, issue #52", async t => { 511 | let elev = new Eleventy("./test/permalink-string/", "./test/permalink-string/_site", { 512 | configPath: "./test/permalink-string/eleventy.config.js" 513 | }); 514 | 515 | let results = await elev.toJSON(); 516 | let [result1, result2] = results.sort((a, b) => a.inputPath < b.inputPath ? -1 : 1); 517 | 518 | t.is(result1.url, "/"); 519 | t.is(result2.url, "/index2.html"); 520 | }); 521 | 522 | test("Using file system bundles, issue #4", async t => { 523 | let elev = new Eleventy("./test/bundler-to-file/", "./test/bundler-to-file/_site", { 524 | configPath: "./test/generic.eleventy.config.js" 525 | }); 526 | 527 | let [result] = await elev.toJSON(); 528 | t.is(normalize(result.content), ``); 529 | 530 | // TODO test actual file output when https://github.com/11ty/eleventy-plugin-bundle/issues/4 is fixed 531 | // let [ passthroughCopy, results ] = await elev.write(); 532 | // let [ result ] = results; 533 | // t.is(normalize(result.content), ``); 534 | 535 | // fs.unlinkSync("./test/bundler-to-file/_site/index.html") 536 | // fs.rmdirSync("./test/bundler-to-file/_site/") 537 | }); 538 | 539 | test("Page with bundled scripts and styles from components", async (t) => { 540 | let elev = new Eleventy( 541 | "./test/script-and-style-buckets/page.webc", 542 | "./test/script-and-style-buckets/_site", 543 | { 544 | configPath: "./test/script-and-style-buckets/eleventy.config.mjs", 545 | } 546 | ); 547 | 548 | let results = await elev.toJSON(); 549 | let [result] = results; 550 | 551 | t.is( 552 | normalize(result.content), 553 | ` 554 | 555 | 556 | 557 | 558 | 559 | 560 | 563 | 564 | 565 | 566 |

Hello everyone!

567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | ` 578 | ); 579 | }); 580 | --------------------------------------------------------------------------------