@11ty/eleventy-plugin-webc 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +12 -0
- package/README.md +105 -41
- package/eleventyWebcPlugin.js +14 -131
- package/package.json +5 -5
- package/src/eleventyWebcTemplate.js +138 -0
- package/src/eleventyWebcTransform.js +40 -0
- package/src/incremental.js +1 -0
- package/test/sample-1/_layouts/layout.webc +0 -17
- package/test/sample-1/eleventy.config.js +0 -12
- package/test/sample-1/page.webc +0 -9
- package/test/sample-2/_includes/component.webc +0 -1
- package/test/sample-2/_layouts/layout.webc +0 -17
- package/test/sample-2/eleventy.config.js +0 -15
- package/test/sample-2/page.webc +0 -9
- package/test/sample-page-global-components/_includes/page1-components/say-hello.webc +0 -1
- package/test/sample-page-global-components/_layouts/layout.webc +0 -17
- package/test/sample-page-global-components/eleventy.config.js +0 -12
- package/test/sample-page-global-components/page.webc +0 -11
- package/test/sample-page-global-components-relative-to-inputpath/_includes/page1-components/say-hello.webc +0 -1
- package/test/sample-page-global-components-relative-to-inputpath/_layouts/layout.webc +0 -17
- package/test/sample-page-global-components-relative-to-inputpath/eleventy.config.js +0 -12
- package/test/sample-page-global-components-relative-to-inputpath/page.webc +0 -11
- package/test/test.js +0 -131
package/.editorconfig
ADDED
package/README.md
CHANGED
|
@@ -8,12 +8,9 @@ Adds support for [WebC, the single file web component format](https://github.com
|
|
|
8
8
|
|
|
9
9
|
* All of the [framework-independent WebC features](https://github.com/11ty/webc#features) out of the box.
|
|
10
10
|
* First-class incremental support (for page templates, components, and Eleventy layouts): use with `--incremental`
|
|
11
|
-
* Tired of importing components?
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* Easiliy 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.
|
|
15
|
-
* Components can roll up CSS/JS assets to arbitrary buckets for custom use!
|
|
16
|
-
* For more complex templating needs, you _can_ render any existing Eleventy template syntax inside of WebC.
|
|
11
|
+
* Tired of importing components? Use our new global or per-page no-import components.
|
|
12
|
+
* 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.
|
|
13
|
+
* For more complex templating needs, you _can_ render any existing Eleventy template syntax (Liquid, markdown, Nunjucks, etc.) inside of WebC.
|
|
17
14
|
* Works great with out of the box with [is-land](https://www.11ty.dev/docs/plugins/partial-hydration/)
|
|
18
15
|
|
|
19
16
|
## ➡ [Documentation](https://www.11ty.dev/docs/languages/webc/)
|
|
@@ -29,17 +26,13 @@ Adds support for [WebC, the single file web component format](https://github.com
|
|
|
29
26
|
|
|
30
27
|
## Installation
|
|
31
28
|
|
|
32
|
-
|
|
29
|
+
It’s on [npm at `@11ty/eleventy-plugin-webc`](https://www.npmjs.com/package/@11ty/eleventy-plugin-webc)!
|
|
33
30
|
|
|
34
|
-
<!--
|
|
35
31
|
```
|
|
36
32
|
npm install @11ty/eleventy-plugin-webc
|
|
37
33
|
```
|
|
38
|
-
-->
|
|
39
34
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
Enable the plugin in your Eleventy configuration file:
|
|
35
|
+
To add support for `.webc` files in Eleventy, add the plugin in your Eleventy configuration file:
|
|
43
36
|
|
|
44
37
|
```js
|
|
45
38
|
const pluginWebc = require("@11ty/eleventy-plugin-webc");
|
|
@@ -49,7 +42,43 @@ module.exports = function(eleventyConfig) {
|
|
|
49
42
|
};
|
|
50
43
|
```
|
|
51
44
|
|
|
52
|
-
|
|
45
|
+
<details>
|
|
46
|
+
<summary><strong>Full options list</strong></summary>
|
|
47
|
+
|
|
48
|
+
```js
|
|
49
|
+
const pluginWebc = require("@11ty/eleventy-plugin-webc");
|
|
50
|
+
|
|
51
|
+
module.exports = function(eleventyConfig) {
|
|
52
|
+
eleventyConfig.addPlugin(pluginWebc, {
|
|
53
|
+
// Glob to find no-import global components
|
|
54
|
+
components: false,
|
|
55
|
+
// components: "_includes/components/**/*.webc",
|
|
56
|
+
|
|
57
|
+
// Adds an Eleventy WebC transform to process all HTML output
|
|
58
|
+
useTransform: false,
|
|
59
|
+
|
|
60
|
+
// Additional global data used in the Eleventy WebC transform
|
|
61
|
+
transformData: {},
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
</details>
|
|
67
|
+
|
|
68
|
+
### How to use it
|
|
69
|
+
|
|
70
|
+
There are a few different ways to use WebC in Eleventy:
|
|
71
|
+
|
|
72
|
+
1. Add a new `.webc` file to your Eleventy input directory
|
|
73
|
+
1. Use the [Render plugin](https://www.11ty.dev/docs/plugins/render/) in an existing template
|
|
74
|
+
1. Preprocess all HTML input as WebC
|
|
75
|
+
1. Use the WebC Eleventy transform to process all of the HTML output (disabled by default)
|
|
76
|
+
|
|
77
|
+
#### Add New `.webc` files
|
|
78
|
+
|
|
79
|
+
Adding the plugin will enable support for `.webc` files in your Eleventy project. Just make a new `.webc` HTML file in your Eleventy input directory and Eleventy will process it for you! If you’re using [global components](#components), don’t forget to point the plugin to your components directory!
|
|
80
|
+
|
|
81
|
+
Notably, `.webc` files will operate [WebC in bundler mode](https://github.com/11ty/webc#aggregating-css-and-js), [aggregating the CSS and JS](#css-and-js) in use on each individual page to create a bundle of the assets in use on the page.
|
|
53
82
|
|
|
54
83
|
```js
|
|
55
84
|
const pluginWebc = require("@11ty/eleventy-plugin-webc");
|
|
@@ -57,7 +86,41 @@ const pluginWebc = require("@11ty/eleventy-plugin-webc");
|
|
|
57
86
|
module.exports = function(eleventyConfig) {
|
|
58
87
|
eleventyConfig.addPlugin(pluginWebc, {
|
|
59
88
|
// Glob to find no-import global components
|
|
60
|
-
components: "_includes/components/**/*.webc",
|
|
89
|
+
components: "_includes/components/**/*.webc",
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
#### Use the Render plugin
|
|
95
|
+
|
|
96
|
+
Using Eleventy’s built-in [Render plugin](https://www.11ty.dev/docs/plugins/render/) with WebC also works great.
|
|
97
|
+
|
|
98
|
+
Consider this example Nunjucks or Liquid template:
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
{% renderTemplate "webc" %}
|
|
102
|
+
<my-custom-component></my-custom-component>
|
|
103
|
+
{% endrenderTemplate %}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### Preprocess all HTML input as WebC
|
|
107
|
+
|
|
108
|
+
You can use the configuration option to change the default HTML preprocessor (from `liquid`) to `webc`. This might look like `htmlTemplateEngine: "webc"`. Read more on the [Eleventy documentation: Default Template Engine for HTML Files](https://www.11ty.dev/docs/config/#default-template-engine-for-html-files).
|
|
109
|
+
|
|
110
|
+
#### Use the global WebC Eleventy transform
|
|
111
|
+
|
|
112
|
+
This is a catch-all option to let WebC process _all_ `.html` output files in your project. [Read more about Eleventy transforms.](https://www.11ty.dev/docs/config/#transforms) This method is useful when you want to get up and running with WebC on an existing project quickly (but is also the slowest method for build-performance).
|
|
113
|
+
|
|
114
|
+
The WebC Eleventy transform operates WebC in non-bundler mode, which means that it does process WebC but _does not_ aggregate JS or CSS on the page.
|
|
115
|
+
|
|
116
|
+
The transform is disabled by default, you will need to add a configuration option to enable it:
|
|
117
|
+
|
|
118
|
+
```js
|
|
119
|
+
const pluginWebc = require("@11ty/eleventy-plugin-webc");
|
|
120
|
+
|
|
121
|
+
module.exports = function(eleventyConfig) {
|
|
122
|
+
eleventyConfig.addPlugin(pluginWebc, {
|
|
123
|
+
useTransform: true,
|
|
61
124
|
});
|
|
62
125
|
};
|
|
63
126
|
```
|
|
@@ -116,8 +179,8 @@ layout: "my-layout.webc"
|
|
|
116
179
|
There are a few ways to use web components here:
|
|
117
180
|
|
|
118
181
|
1. You can use `webc:import` inside of your components to import another component directly.
|
|
119
|
-
2. Use global no-import components specified in your config file
|
|
120
|
-
3. Specify a glob of no-import components at a directory or template level in the data cascade
|
|
182
|
+
2. Use global no-import components specified in your config file.
|
|
183
|
+
3. Specify a glob of no-import components at a directory or template level in the data cascade.
|
|
121
184
|
|
|
122
185
|
Notably, WebC components are not restricted to the same naming limitations as custom elements (they do not require a dash in the name). The [WebC documentation has all of the detail on how to use and configure WebC components](https://github.com/11ty/webc#html-imports-kidding-kinda).
|
|
123
186
|
|
|
@@ -125,9 +188,20 @@ Notably, WebC components are not restricted to the same naming limitations as cu
|
|
|
125
188
|
|
|
126
189
|
Use the `components` property in the Full options list in your Eleventy configuration file to specify project-wide WebC component files available for use in any page.
|
|
127
190
|
|
|
191
|
+
```js
|
|
192
|
+
const pluginWebc = require("@11ty/eleventy-plugin-webc");
|
|
193
|
+
|
|
194
|
+
module.exports = function(eleventyConfig) {
|
|
195
|
+
eleventyConfig.addPlugin(pluginWebc, {
|
|
196
|
+
// Glob to find no-import global components
|
|
197
|
+
components: "_includes/components/**/*.webc",
|
|
198
|
+
});
|
|
199
|
+
};
|
|
200
|
+
```
|
|
201
|
+
|
|
128
202
|
#### Specify no-import Components in the Data Cascade
|
|
129
203
|
|
|
130
|
-
You can also use and configure specific components as part of the data cascade as well (
|
|
204
|
+
You can also use and configure specific components as part of the data cascade as well (scoped to a folder or a template) by assigning a glob (or array of globs) to `webc.components`, like so:
|
|
131
205
|
|
|
132
206
|
`my-directory/my-page.webc`:
|
|
133
207
|
|
|
@@ -142,7 +216,7 @@ webc:
|
|
|
142
216
|
|
|
143
217
|
* Usage warning: By default these paths are relative to the template file. If you’re setting this in the data cascade to apply multiple child folders deep, it might be better to use the global no-import components option above OR to use `~/` as a prefix (e.g. `~/my-directory/webc/*.webc`) to alias to the project’s root directory.
|
|
144
218
|
|
|
145
|
-
### CSS and JS
|
|
219
|
+
### CSS and JS (Bundler mode)
|
|
146
220
|
|
|
147
221
|
[Eleventy Layouts](https://www.11ty.dev/docs/layouts/) can bundle any specific page’s assets (CSS and JS used by components on the page). These are automatically rolled up when a component uses `<script>` or `<style>`. You can use this to easily implement component-driven Critical CSS.
|
|
148
222
|
|
|
@@ -233,32 +307,22 @@ You can also use this out of the box with Eleventy’s [`is-land` component for
|
|
|
233
307
|
|
|
234
308
|
At the component level, components can declare their own is-land loading conditions.
|
|
235
309
|
|
|
236
|
-
_(Some features here require `is-land` 1.0.1 or newer)_
|
|
237
|
-
|
|
238
310
|
`_includes/webc/my-webc-component.webc`:
|
|
239
311
|
|
|
240
312
|
```html
|
|
241
313
|
<is-land on:visible>
|
|
242
|
-
<
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
<
|
|
254
|
-
|
|
255
|
-
/* This is some is-land on-interaction CSS */
|
|
256
|
-
</style>
|
|
257
|
-
<script type="module/island" webc:keep>
|
|
258
|
-
console.log("This is some is-land on-interaction JavaScript");
|
|
259
|
-
</script>
|
|
260
|
-
|
|
261
|
-
<link rel="stylesheet" media="style/island" href="some-arbitrary-css.css">
|
|
262
|
-
<script type="module/island" src="some-arbitrary-js.js"></script>
|
|
314
|
+
<template data-island>
|
|
315
|
+
<!-- CSS -->
|
|
316
|
+
<style webc:keep>
|
|
317
|
+
/* This is on-visible CSS */
|
|
318
|
+
</style>
|
|
319
|
+
<link rel="stylesheet" href="some-arbitrary-css.css" webc:keep>
|
|
320
|
+
|
|
321
|
+
<!-- JS -->
|
|
322
|
+
<script type="module" webc:keep>
|
|
323
|
+
console.log("This is on-visible JavaScript");
|
|
324
|
+
</script>
|
|
325
|
+
<script type="module" src="some-arbitrary-js.js" webc:keep></script>
|
|
326
|
+
</template>
|
|
263
327
|
</is-land>
|
|
264
328
|
```
|
package/eleventyWebcPlugin.js
CHANGED
|
@@ -1,23 +1,6 @@
|
|
|
1
1
|
const pkg = require("./package.json");
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
const EleventyRenderManager = EleventyRenderPlugin.RenderManager;
|
|
5
|
-
|
|
6
|
-
const CodeManager = require("./src/codeManager.js");
|
|
7
|
-
const WebCIncremental = require("./src/incremental.js");
|
|
8
|
-
|
|
9
|
-
function relativePath(inputPath, newGlob) {
|
|
10
|
-
// project root
|
|
11
|
-
if(newGlob.startsWith("~/")) {
|
|
12
|
-
let rootRelativePath = "." + newGlob.slice(1);
|
|
13
|
-
return rootRelativePath;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
let { dir } = path.parse(inputPath);
|
|
17
|
-
// globs must have forward slashes (even on Windows)
|
|
18
|
-
let templateRelativePath = path.join(dir, newGlob).split(path.sep).join("/");
|
|
19
|
-
return templateRelativePath;
|
|
20
|
-
}
|
|
2
|
+
const templatePlugin = require("./src/eleventyWebcTemplate.js");
|
|
3
|
+
const transformPlugin = require("./src/eleventyWebcTransform.js");
|
|
21
4
|
|
|
22
5
|
/* TODO
|
|
23
6
|
* Use JavaScript functions or universal shortcodes as built-in components?
|
|
@@ -30,126 +13,26 @@ module.exports = function(eleventyConfig, options = {}) {
|
|
|
30
13
|
console.log( `WARN: Eleventy Plugin (${pkg.name}) Compatibility: ${e.message}` );
|
|
31
14
|
}
|
|
32
15
|
|
|
16
|
+
let filters = Object.assign({
|
|
17
|
+
css: "webcGetCss",
|
|
18
|
+
js: "webcGetJs",
|
|
19
|
+
}, options.filters);
|
|
20
|
+
|
|
33
21
|
options = Object.assign({
|
|
34
22
|
components: false, // glob for no-import global components
|
|
23
|
+
useTransform: false, // global transform
|
|
24
|
+
transformData: {}, // extra global data for transforms specifically
|
|
35
25
|
}, options);
|
|
36
26
|
|
|
27
|
+
options.filters = filters;
|
|
28
|
+
|
|
37
29
|
if(options.components) {
|
|
38
30
|
eleventyConfig.addWatchTarget(options.components);
|
|
39
31
|
}
|
|
40
32
|
|
|
41
|
-
eleventyConfig
|
|
42
|
-
|
|
43
|
-
let renderManager = new EleventyRenderManager();
|
|
44
|
-
let cssManager = new CodeManager();
|
|
45
|
-
let jsManager = new CodeManager();
|
|
46
|
-
|
|
47
|
-
eleventyConfig.on("eleventy.before", () => {
|
|
48
|
-
cssManager.reset();
|
|
49
|
-
jsManager.reset();
|
|
50
|
-
});
|
|
33
|
+
templatePlugin(eleventyConfig, options);
|
|
51
34
|
|
|
52
|
-
|
|
53
|
-
|
|
35
|
+
if(options.useTransform) {
|
|
36
|
+
transformPlugin(eleventyConfig, options);
|
|
54
37
|
}
|
|
55
|
-
|
|
56
|
-
function getJs(pageUrl, bucket = "default") {
|
|
57
|
-
return jsManager.getForPage(pageUrl, bucket);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// TODO
|
|
61
|
-
// eleventyConfig.addFilter("webcGetCSS", (url, bucket) => getCss(url, bucket));
|
|
62
|
-
// eleventyConfig.addFilter("webcGetJS", (url, bucket) => getJs(url, bucket));
|
|
63
|
-
|
|
64
|
-
let incremental = new WebCIncremental();
|
|
65
|
-
|
|
66
|
-
eleventyConfig.on("eleventy.layouts", layouts => {
|
|
67
|
-
incremental.setLayouts(layouts);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
eleventyConfig.on("eleventy.before", () => {
|
|
71
|
-
incremental.setComponents(options.components);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
eleventyConfig.addExtension("webc", {
|
|
75
|
-
outputFileExtension: "html",
|
|
76
|
-
|
|
77
|
-
init: async function() {
|
|
78
|
-
// For ESM in CJS
|
|
79
|
-
let e = await import("@11ty/webc");
|
|
80
|
-
incremental.setWebC(e.WebC);
|
|
81
|
-
|
|
82
|
-
if(incremental.needsComponents()) {
|
|
83
|
-
incremental.setComponents(options.components);
|
|
84
|
-
}
|
|
85
|
-
},
|
|
86
|
-
|
|
87
|
-
isIncrementalMatch: function (incrementalFilePath) {
|
|
88
|
-
// Eleventy layouts don’t appear directly in the WebC component graph, so we use the `eleventy.layouts` map here
|
|
89
|
-
if(incremental.isTemplateUsingLayout(this.inputPath, incrementalFilePath)) {
|
|
90
|
-
return true;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
let {page, setup} = incremental.get(this.inputPath);
|
|
94
|
-
if(page && setup) {
|
|
95
|
-
if(page.getComponents(setup).includes(incrementalFilePath)) {
|
|
96
|
-
return true;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return false;
|
|
101
|
-
},
|
|
102
|
-
|
|
103
|
-
compile: async function(inputContent, inputPath) {
|
|
104
|
-
let page = incremental.add(inputContent, inputPath);
|
|
105
|
-
|
|
106
|
-
page.setHelper("getCSS", function(url, bucket) {
|
|
107
|
-
return getCss(url, bucket);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
page.setHelper("getJS", function(url, bucket) {
|
|
111
|
-
return getJs(url, bucket);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
page.setTransform("11ty", async function(content) {
|
|
115
|
-
let syntax = this["11ty:type"];
|
|
116
|
-
if(syntax) {
|
|
117
|
-
let fn = await renderManager.compile(content, syntax);
|
|
118
|
-
return renderManager.render(fn, this, {});
|
|
119
|
-
}
|
|
120
|
-
return content;
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
return async (data) => {
|
|
124
|
-
let setupObject = { data };
|
|
125
|
-
if(data.webc?.components) {
|
|
126
|
-
setupObject.components = incremental.getComponentMap(relativePath(data.page.inputPath, data.webc.components));
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
let setup = await page.setup(setupObject);
|
|
130
|
-
incremental.addSetup(inputPath, setup);
|
|
131
|
-
|
|
132
|
-
let { ast, serializer } = setup;
|
|
133
|
-
let { html, css, js, buckets } = await serializer.compile(ast);
|
|
134
|
-
|
|
135
|
-
cssManager.addToPage(data.page.url, css, "default");
|
|
136
|
-
|
|
137
|
-
if(buckets.css) {
|
|
138
|
-
for(let bucket in buckets.css) {
|
|
139
|
-
cssManager.addToPage(data.page.url, buckets.css[bucket], bucket);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
jsManager.addToPage(data.page.url, js, "default");
|
|
144
|
-
|
|
145
|
-
if(buckets.js) {
|
|
146
|
-
for(let bucket in buckets.js) {
|
|
147
|
-
jsManager.addToPage(data.page.url, buckets.js[bucket], bucket);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return html;
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
38
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@11ty/eleventy-plugin-webc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "WebC support for Eleventy adds for Single File Web Components",
|
|
5
5
|
"main": "eleventyWebcPlugin.js",
|
|
6
6
|
"scripts": {
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
"engines": {
|
|
14
14
|
"node": ">=14.18"
|
|
15
15
|
},
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
"11ty": {
|
|
17
|
+
"compatibility": ">=2.0.0-canary.16"
|
|
18
|
+
},
|
|
19
19
|
"funding": {
|
|
20
20
|
"type": "opencollective",
|
|
21
21
|
"url": "https://opencollective.com/11ty"
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@11ty/eleventy": "^2.0.0-canary.16",
|
|
42
|
-
"@11ty/webc": "^0.
|
|
42
|
+
"@11ty/webc": "^0.4.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"ava": "^4.3.3"
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
|
|
3
|
+
const { EleventyRenderPlugin } = require("@11ty/eleventy");
|
|
4
|
+
const EleventyRenderManager = EleventyRenderPlugin.RenderManager;
|
|
5
|
+
|
|
6
|
+
const CodeManager = require("./codeManager.js");
|
|
7
|
+
const WebCIncremental = require("./incremental.js");
|
|
8
|
+
|
|
9
|
+
function relativePath(inputPath, newGlob) {
|
|
10
|
+
// project root
|
|
11
|
+
if(newGlob.startsWith("~/")) {
|
|
12
|
+
let rootRelativePath = "." + newGlob.slice(1);
|
|
13
|
+
return rootRelativePath;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let { dir } = path.parse(inputPath);
|
|
17
|
+
// globs must have forward slashes (even on Windows)
|
|
18
|
+
let templateRelativePath = path.join(dir, newGlob).split(path.sep).join("/");
|
|
19
|
+
return templateRelativePath;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = function(eleventyConfig, options = {}) {
|
|
23
|
+
eleventyConfig.addTemplateFormats("webc");
|
|
24
|
+
|
|
25
|
+
let renderManager = new EleventyRenderManager();
|
|
26
|
+
let cssManager = new CodeManager();
|
|
27
|
+
let jsManager = new CodeManager();
|
|
28
|
+
let incremental = new WebCIncremental();
|
|
29
|
+
|
|
30
|
+
eleventyConfig.on("eleventy.before", () => {
|
|
31
|
+
cssManager.reset();
|
|
32
|
+
jsManager.reset();
|
|
33
|
+
incremental.setComponents(options.components);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
function getCss(pageUrl, bucket = "default") {
|
|
37
|
+
return cssManager.getForPage(pageUrl, bucket);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getJs(pageUrl, bucket = "default") {
|
|
41
|
+
return jsManager.getForPage(pageUrl, bucket);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if(options.filters.css) {
|
|
45
|
+
eleventyConfig.addFilter(options.filters.css, (url, bucket) => getCss(url, bucket));
|
|
46
|
+
}
|
|
47
|
+
if(options.filters.js) {
|
|
48
|
+
eleventyConfig.addFilter(options.filters.js, (url, bucket) => getJs(url, bucket));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
eleventyConfig.on("eleventy.layouts", layouts => {
|
|
52
|
+
incremental.setLayouts(layouts);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
eleventyConfig.addExtension("webc", {
|
|
56
|
+
outputFileExtension: "html",
|
|
57
|
+
|
|
58
|
+
init: async function() {
|
|
59
|
+
// For ESM in CJS
|
|
60
|
+
let { WebC } = await import("@11ty/webc");
|
|
61
|
+
incremental.setWebC(WebC);
|
|
62
|
+
|
|
63
|
+
if(incremental.needsComponents()) {
|
|
64
|
+
incremental.setComponents(options.components);
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
isIncrementalMatch: function (incrementalFilePath) {
|
|
69
|
+
// Eleventy layouts don’t appear directly in the WebC component graph, so we use the `eleventy.layouts` map here
|
|
70
|
+
if(incremental.isTemplateUsingLayout(this.inputPath, incrementalFilePath)) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let {page, setup} = incremental.get(this.inputPath);
|
|
75
|
+
if(page && setup) {
|
|
76
|
+
if(page.getComponents(setup).includes(incrementalFilePath)) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return false;
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
compile: async function(inputContent, inputPath) {
|
|
85
|
+
let page = incremental.add(inputContent, inputPath);
|
|
86
|
+
|
|
87
|
+
// TODO add all universal filters/JavaScript functions as helpers here:
|
|
88
|
+
|
|
89
|
+
page.setHelper("getCSS", function(url, bucket) {
|
|
90
|
+
return getCss(url, bucket);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
page.setHelper("getJS", function(url, bucket) {
|
|
94
|
+
return getJs(url, bucket);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
page.setTransform("11ty", async function(content) {
|
|
98
|
+
let syntax = this["11ty:type"];
|
|
99
|
+
if(syntax) {
|
|
100
|
+
let fn = await renderManager.compile(content, syntax);
|
|
101
|
+
return renderManager.render(fn, this, {});
|
|
102
|
+
}
|
|
103
|
+
return content;
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return async (data) => {
|
|
107
|
+
let setupObject = { data };
|
|
108
|
+
if(data.webc?.components) {
|
|
109
|
+
setupObject.components = incremental.getComponentMap(relativePath(data.page.inputPath, data.webc.components));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let setup = await page.setup(setupObject);
|
|
113
|
+
incremental.addSetup(inputPath, setup);
|
|
114
|
+
|
|
115
|
+
let { ast, serializer } = setup;
|
|
116
|
+
let { html, css, js, buckets } = await serializer.compile(ast);
|
|
117
|
+
|
|
118
|
+
cssManager.addToPage(data.page.url, css, "default");
|
|
119
|
+
|
|
120
|
+
if(buckets.css) {
|
|
121
|
+
for(let bucket in buckets.css) {
|
|
122
|
+
cssManager.addToPage(data.page.url, buckets.css[bucket], bucket);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
jsManager.addToPage(data.page.url, js, "default");
|
|
127
|
+
|
|
128
|
+
if(buckets.js) {
|
|
129
|
+
for(let bucket in buckets.js) {
|
|
130
|
+
jsManager.addToPage(data.page.url, buckets.js[bucket], bucket);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return html;
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
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
|
+
eleventyConfig.addTransform("@11ty/eleventy-plugin-webc", async function(content) {
|
|
9
|
+
// Skip non-.html output
|
|
10
|
+
// Skip .webc input
|
|
11
|
+
if(this.inputPath.endsWith(".webc") || !this.outputPath?.endsWith(".html")) {
|
|
12
|
+
return content;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// TODO prevent double processing with the Render plugin with WebC
|
|
16
|
+
// https://www.11ty.dev/docs/plugins/render/
|
|
17
|
+
try {
|
|
18
|
+
const { WebC } = await import("@11ty/webc");
|
|
19
|
+
let page = new WebC();
|
|
20
|
+
|
|
21
|
+
if(componentsMap === false && options.components) {
|
|
22
|
+
componentsMap = WebC.getComponentsMap(options.components); // "./_includes/webc/*.webc"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
page.defineComponents(componentsMap);
|
|
26
|
+
page.setContent(content, this.outputPath);
|
|
27
|
+
|
|
28
|
+
let { html } = await page.compile({
|
|
29
|
+
// global data
|
|
30
|
+
data: options.transformData
|
|
31
|
+
});
|
|
32
|
+
return html;
|
|
33
|
+
} catch(e) {
|
|
34
|
+
console.error( `[11ty/eleventy-plugin-webc] Error transforming ${this.inputPath}`, e );
|
|
35
|
+
throw e;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return content;
|
|
39
|
+
});
|
|
40
|
+
}
|
package/src/incremental.js
CHANGED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<meta name="description" content="">
|
|
7
|
-
<title></title>
|
|
8
|
-
<style @html="this.getCSS(this.page.url)"></style>
|
|
9
|
-
<script @html="this.getJS(this.page.url)"></script>
|
|
10
|
-
</head>
|
|
11
|
-
<body>
|
|
12
|
-
<template webc:nokeep @html="this.content"></template>
|
|
13
|
-
|
|
14
|
-
<style @html="this.getCSS(this.page.url, 'defer')"></style>
|
|
15
|
-
<script @html="this.getJS(this.page.url, 'defer')"></script>
|
|
16
|
-
</body>
|
|
17
|
-
</html>
|
package/test/sample-1/page.webc
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
This is a component.
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<meta name="description" content="">
|
|
7
|
-
<title></title>
|
|
8
|
-
<style @html="this.getCSS(this.page.url)"></style>
|
|
9
|
-
<script @html="this.getJS(this.page.url)"></script>
|
|
10
|
-
</head>
|
|
11
|
-
<body>
|
|
12
|
-
<template webc:nokeep @html="this.content"></template>
|
|
13
|
-
|
|
14
|
-
<style @html="this.getCSS(this.page.url, 'defer')"></style>
|
|
15
|
-
<script @html="this.getJS(this.page.url, 'defer')"></script>
|
|
16
|
-
</body>
|
|
17
|
-
</html>
|
|
@@ -1,15 +0,0 @@
|
|
|
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
|
-
}
|
package/test/sample-2/page.webc
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
HELLO
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<meta name="description" content="">
|
|
7
|
-
<title></title>
|
|
8
|
-
<style @html="this.getCSS(this.page.url)"></style>
|
|
9
|
-
<script @html="this.getJS(this.page.url)"></script>
|
|
10
|
-
</head>
|
|
11
|
-
<body>
|
|
12
|
-
<template webc:nokeep @html="this.content"></template>
|
|
13
|
-
|
|
14
|
-
<style @html="this.getCSS(this.page.url, 'defer')"></style>
|
|
15
|
-
<script @html="this.getJS(this.page.url, 'defer')"></script>
|
|
16
|
-
</body>
|
|
17
|
-
</html>
|
|
@@ -1,11 +0,0 @@
|
|
|
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
|
-
<say-hello></say-hello>
|
|
8
|
-
<say-hello></say-hello>
|
|
9
|
-
WHO IS THIS
|
|
10
|
-
hi
|
|
11
|
-
<span @html="this.frontmatterdata"></span>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
HELLO
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<meta name="description" content="">
|
|
7
|
-
<title></title>
|
|
8
|
-
<style @html="this.getCSS(this.page.url)"></style>
|
|
9
|
-
<script @html="this.getJS(this.page.url)"></script>
|
|
10
|
-
</head>
|
|
11
|
-
<body>
|
|
12
|
-
<template webc:nokeep @html="this.content"></template>
|
|
13
|
-
|
|
14
|
-
<style @html="this.getCSS(this.page.url, 'defer')"></style>
|
|
15
|
-
<script @html="this.getJS(this.page.url, 'defer')"></script>
|
|
16
|
-
</body>
|
|
17
|
-
</html>
|
package/test/test.js
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
const test = require("ava");
|
|
2
|
-
const Eleventy = require("@11ty/eleventy");
|
|
3
|
-
|
|
4
|
-
test("Sample page", async t => {
|
|
5
|
-
let elev = new Eleventy("./test/sample-1/page.webc", "./test/sample-1/_site", {
|
|
6
|
-
configPath: "./test/sample-1/eleventy.config.js"
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
let results = await elev.toJSON();
|
|
10
|
-
let [result] = results;
|
|
11
|
-
|
|
12
|
-
t.is(result.content.trim(), `<!doctype html>
|
|
13
|
-
<html lang="en">
|
|
14
|
-
<head>
|
|
15
|
-
<meta charset="utf-8">
|
|
16
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
17
|
-
<meta name="description" content>
|
|
18
|
-
<title></title>
|
|
19
|
-
<style></style>
|
|
20
|
-
<script></script>
|
|
21
|
-
</head>
|
|
22
|
-
<body>
|
|
23
|
-
<say-hello></say-hello>
|
|
24
|
-
<say-hello></say-hello>
|
|
25
|
-
WHO IS THIS
|
|
26
|
-
hi
|
|
27
|
-
<span>HELLO FROM FRONT MATTER</span>
|
|
28
|
-
|
|
29
|
-
<style></style>
|
|
30
|
-
<script></script>
|
|
31
|
-
|
|
32
|
-
</body>
|
|
33
|
-
</html>`);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test("Sample page with global component", async t => {
|
|
37
|
-
let elev = new Eleventy("./test/sample-2/page.webc", "./test/sample-2/_site", {
|
|
38
|
-
configPath: "./test/sample-2/eleventy.config.js"
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
let results = await elev.toJSON();
|
|
42
|
-
let [result] = results;
|
|
43
|
-
|
|
44
|
-
t.is(result.content.trim(), `<!doctype html>
|
|
45
|
-
<html lang="en">
|
|
46
|
-
<head>
|
|
47
|
-
<meta charset="utf-8">
|
|
48
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
49
|
-
<meta name="description" content>
|
|
50
|
-
<title></title>
|
|
51
|
-
<style></style>
|
|
52
|
-
<script></script>
|
|
53
|
-
</head>
|
|
54
|
-
<body>
|
|
55
|
-
This is a component.
|
|
56
|
-
This is a component.
|
|
57
|
-
WHO IS THIS
|
|
58
|
-
hi
|
|
59
|
-
<span>HELLO FROM FRONT MATTER</span>
|
|
60
|
-
|
|
61
|
-
<style></style>
|
|
62
|
-
<script></script>
|
|
63
|
-
|
|
64
|
-
</body>
|
|
65
|
-
</html>`);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test("Page with front matter no-import components", async t => {
|
|
69
|
-
let elev = new Eleventy("./test/sample-page-global-components/page.webc", "./test/sample-page-global-components/_site", {
|
|
70
|
-
configPath: "./test/sample-page-global-components/eleventy.config.js"
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
let results = await elev.toJSON();
|
|
74
|
-
let [result] = results;
|
|
75
|
-
|
|
76
|
-
t.is(result.content.trim(), `<!doctype html>
|
|
77
|
-
<html lang="en">
|
|
78
|
-
<head>
|
|
79
|
-
<meta charset="utf-8">
|
|
80
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
81
|
-
<meta name="description" content>
|
|
82
|
-
<title></title>
|
|
83
|
-
<style></style>
|
|
84
|
-
<script></script>
|
|
85
|
-
</head>
|
|
86
|
-
<body>
|
|
87
|
-
HELLO
|
|
88
|
-
HELLO
|
|
89
|
-
WHO IS THIS
|
|
90
|
-
hi
|
|
91
|
-
<span>HELLO FROM FRONT MATTER</span>
|
|
92
|
-
|
|
93
|
-
<style></style>
|
|
94
|
-
<script></script>
|
|
95
|
-
|
|
96
|
-
</body>
|
|
97
|
-
</html>`);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
test("Page with front matter no-import components (relative to input path)", async t => {
|
|
102
|
-
let elev = new Eleventy("./test/sample-page-global-components-relative-to-inputpath/page.webc", "./test/sample-page-global-components-relative-to-inputpath/_site", {
|
|
103
|
-
configPath: "./test/sample-page-global-components-relative-to-inputpath/eleventy.config.js"
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
let results = await elev.toJSON();
|
|
107
|
-
let [result] = results;
|
|
108
|
-
|
|
109
|
-
t.is(result.content.trim(), `<!doctype html>
|
|
110
|
-
<html lang="en">
|
|
111
|
-
<head>
|
|
112
|
-
<meta charset="utf-8">
|
|
113
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
114
|
-
<meta name="description" content>
|
|
115
|
-
<title></title>
|
|
116
|
-
<style></style>
|
|
117
|
-
<script></script>
|
|
118
|
-
</head>
|
|
119
|
-
<body>
|
|
120
|
-
HELLO
|
|
121
|
-
HELLO
|
|
122
|
-
WHO IS THIS
|
|
123
|
-
hi
|
|
124
|
-
<span>HELLO FROM FRONT MATTER</span>
|
|
125
|
-
|
|
126
|
-
<style></style>
|
|
127
|
-
<script></script>
|
|
128
|
-
|
|
129
|
-
</body>
|
|
130
|
-
</html>`);
|
|
131
|
-
});
|