eleventy-plugin-vento 2.2.0 → 2.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.
@@ -1,3 +1,13 @@
1
1
  {
2
- "extends": ["github>noelforte/renovate-config", "group:allNonMajor"]
2
+ "extends": ["github>noelforte/renovate-config"],
3
+ "packageRules": [
4
+ {
5
+ "matchDepTypes": ["dependencies"],
6
+ "semanticCommitType": "upstream",
7
+ "semanticCommitScope": null
8
+ },
9
+ {
10
+ "matchDepTypes": ["devDependencies"]
11
+ }
12
+ ]
3
13
  }
@@ -1,13 +1,9 @@
1
1
  name: CI
2
2
 
3
- on:
4
- push:
5
- branches: [main, dev, alpha, beta, next, renovate/**]
6
- workflow_dispatch:
7
- workflow_call:
3
+ on: push
8
4
 
9
5
  jobs:
10
- test:
6
+ default:
11
7
  runs-on: ubuntu-latest
12
8
  steps:
13
9
  - uses: actions/checkout@v4
@@ -1,15 +1,14 @@
1
- # Run releases every monday and wednesday
2
- # at 12:33 UTC (or manually)
3
-
4
1
  name: Release
5
2
 
6
3
  on:
7
- schedule:
8
- - cron: '33 12 * * 1,3'
9
- workflow_dispatch:
4
+ workflow_run:
5
+ types: [completed]
6
+ workflows: [CI]
7
+ branches: [main, alpha, beta, next]
10
8
 
11
9
  jobs:
12
10
  release:
11
+ if: github.event.workflow_run.conclusion == 'success'
13
12
  runs-on: ubuntu-latest
14
13
  permissions:
15
14
  contents: write # to be able to publish a GitHub release
@@ -24,9 +23,6 @@ jobs:
24
23
  - name: Setup
25
24
  uses: ./.github/actions/setup
26
25
 
27
- - name: Check project for errors
28
- run: pnpm test
29
-
30
26
  - name: Verify provenance attestations and registry signatures
31
27
  run: npm audit signatures
32
28
 
package/.node-version CHANGED
@@ -1 +1 @@
1
- 20.14.0
1
+ 20.15.1
@@ -0,0 +1,16 @@
1
+ {
2
+ "New test suite": {
3
+ "scope": "javascript",
4
+ "prefix": "VentoPluginTest",
5
+ "body": [
6
+ "import { expect, test } from 'vitest';",
7
+ "import { runBuild } from '#test-instance';\n",
8
+ "const results = await runBuild(import.meta.dirname, {",
9
+ "\t${1:eleventy(configApi) {\\}},",
10
+ "\t${2:vento: {\\}},",
11
+ "});"
12
+ ],
13
+ "description": "Create a new test suite for eleventy-plugin-vento",
14
+ "isFileTemplate": true
15
+ }
16
+ }
package/VentoPlugin.js CHANGED
@@ -14,6 +14,7 @@
14
14
  */
15
15
 
16
16
  import VentoJs from 'ventojs';
17
+ import { ssr } from '#lib/ssr.js';
17
18
 
18
19
  // Expose autotrim plugin defaults
19
20
  import {
@@ -69,6 +70,9 @@ export function VentoPlugin(eleventyConfig, options = {}) {
69
70
  // Load user-defined plugins into vento
70
71
  for (const plugin of options.plugins) env.use(plugin);
71
72
 
73
+ // Load ssr plugin
74
+ env.use(ssr);
75
+
72
76
  // Add autotrim plugin if enabled
73
77
  if (options.trimTags === true) env.use(autoTrim());
74
78
  else if (options.trimTags) env.use(autoTrim({ tags: options.trimTags }));
package/jsconfig.json CHANGED
@@ -2,6 +2,10 @@
2
2
  "compilerOptions": {
3
3
  "module": "NodeNext",
4
4
  "moduleResolution": "NodeNext",
5
- "baseUrl": "."
5
+ "baseUrl": ".",
6
+ "paths": {
7
+ "#lib/*": ["./lib/*"],
8
+ "#test-instance": ["./tests/get-instance.js"]
9
+ }
6
10
  }
7
11
  }
package/lib/ssr.js ADDED
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @typedef {import('ventojs/src/tokenizer.js').Token} Token
3
+ * @typedef {import('ventojs/src/environment.js').Environment} Environment
4
+ */
5
+
6
+ /** @param {Environment} env */
7
+ export function ssr(env) {
8
+ env.tags.push(ssrTag);
9
+ }
10
+
11
+ /**
12
+ * @param {string} code
13
+ * @param {Environment} env
14
+ * @param {string} output
15
+ * @param {Token[]} tokens
16
+ */
17
+ function ssrTag(env, code, output, _tokens) {
18
+ if (!code.startsWith('!')) return;
19
+
20
+ const compiled = `{{${code.replace(/!(>?)\s+/, '$1 ')} }}`;
21
+
22
+ return `${output} += "${compiled}";`;
23
+ }
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "eleventy-plugin-vento",
3
- "version": "2.2.0",
3
+ "version": "2.4.0",
4
4
  "description": "Adds support for the Vento templating language to Eleventy.",
5
5
  "type": "module",
6
- "packageManager": "pnpm@9.3.0",
6
+ "packageManager": "pnpm@9.5.0",
7
7
  "engines": {
8
8
  "node": ">=16"
9
9
  },
@@ -12,6 +12,10 @@
12
12
  "lint": "eslint **/*.js --config ./eslint.config.js"
13
13
  },
14
14
  "main": "./VentoPlugin.js",
15
+ "imports": {
16
+ "#lib/*": "./lib/*",
17
+ "#test-instance": "./tests/get-instance.js"
18
+ },
15
19
  "exports": {
16
20
  ".": "./VentoPlugin.js"
17
21
  },
@@ -32,15 +36,15 @@
32
36
  },
33
37
  "license": "MIT",
34
38
  "dependencies": {
35
- "ventojs": "0.12.7"
39
+ "ventojs": "0.12.8"
36
40
  },
37
41
  "devDependencies": {
38
- "@11ty/eleventy": "3.0.0-alpha.10",
39
- "conventional-changelog-conventionalcommits": "^8.0.0",
40
- "eslint": "9.4.0",
41
- "eslint-config-flat-gitignore": "0.1.5",
42
+ "@11ty/eleventy": "3.0.0-alpha.14",
43
+ "conventional-changelog-conventionalcommits": "8.0.0",
44
+ "eslint": "9.6.0",
45
+ "eslint-config-flat-gitignore": "0.1.7",
42
46
  "eslint-config-prettier": "9.1.0",
43
- "globals": "15.4.0",
47
+ "globals": "15.8.0",
44
48
  "semantic-release": "24.0.0",
45
49
  "vitest": "1.6.0"
46
50
  },
package/readme.md CHANGED
@@ -1,7 +1,13 @@
1
1
  # eleventy-plugin-vento 🌬️🎈🐀
2
2
 
3
+ <a href="https://github.com/noelforte/eleventy-plugin-vento/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/noelforte/eleventy-plugin-vento/ci.yml?branch=main&style=flat-square&logo=github&logoColor=fff&label=Tests&labelColor=333" alt="Github Actions Status"></a>
4
+ <a href="https://npmjs.com/package/eleventy-plugin-vento"><img src="https://img.shields.io/npm/v/eleventy-plugin-vento?style=flat-square&logo=npm&logoColor=fff&labelColor=333" alt="eleventy-plugin-vento on npm"></a>
5
+ <a href="https://github.com/semantic-release/semantic-release"><img src="https://img.shields.io/badge/semantic--release-Conventional_Commits-fa6673?style=flat-square&logo=semanticrelease&logoColor=fff&labelColor=333" alt="semantic-release"></a>
6
+
3
7
  An [Eleventy](https://11ty.dev/) plugin that adds support for [Vento](https://vento.js.org/) templates.
4
8
 
9
+ ## Contents
10
+
5
11
  [Installing](#installing)<br>
6
12
  [Usage](#usage)<br>
7
13
  [Plugin Options](#plugin-options)<br>
@@ -9,6 +15,7 @@ An [Eleventy](https://11ty.dev/) plugin that adds support for [Vento](https://ve
9
15
  [Vento Plugins](#vento-plugins)<br>
10
16
  [Auto-Trimming Tags](#auto-trimming-tags)<br>
11
17
  [JavaScript Helpers](#javascript-helpers)<br>
18
+ [Hybrid Rendering](#hybrid-rendering)<br>
12
19
 
13
20
  ## Installing
14
21
 
@@ -159,7 +166,7 @@ Alternatively, if you'd rather keep functions namespaced in your page data, pass
159
166
  addPlugin(VentoPlugin, { addHelpers: 'functions' });
160
167
  ```
161
168
 
162
- ```hbs
169
+ ```nunjucks
163
170
  <!-- With the above, this: -->
164
171
  <p>This is something {{ italicize('really') }} important.</p>
165
172
  <p>{{ possumPosse() }}</p>
@@ -176,3 +183,63 @@ eleventyConfig.addPlugin(VentoPlugin, {
176
183
  addHelpers: false,
177
184
  });
178
185
  ```
186
+
187
+ ## Hybrid Rendering
188
+
189
+ Exclusive to this plugin is the ability to skip processing a vento tag entirely and instead preserve the tag in the markup. This could be useful if you're doing some hybrid rendering and would like to defer certain tags from being processed until load time, so they can be rendered on the server.
190
+
191
+ To skip over a tag, add a `!` directly after the opening tag.
192
+
193
+ ```nunjucks
194
+ <!-- This: -->
195
+ {{! doSomeServerSideStuff() }}
196
+
197
+ <!-- Renders as: -->
198
+ {{ doSomeServerSideStuff() }}
199
+ ```
200
+
201
+ Works with JS expressions too:
202
+
203
+ ```nunjucks
204
+ {{!> 2 + 2 }}
205
+
206
+ <!-- Renders as: -->
207
+ {{> 2 + 2 }}
208
+ ```
209
+
210
+ The Vento language offers similar functionality via the [`echo` tag](https://vento.js.org/syntax/print/#echo) which works great for blocks, but can be verbose for 1-off tags.
211
+
212
+ Consider the following:
213
+
214
+ ```nunjucks
215
+ {{ echo }}
216
+ <!-- Will be preserved in output -->
217
+ {{ if someCondition }}
218
+ <p>{{ getServerData }}</p>
219
+ {{ /echo }}
220
+
221
+ <!-- Can't render this without exiting and re-entering echo -->
222
+ <p>Page built on: {{ localBuildTime }}</p>
223
+
224
+ {{ echo }}
225
+ {{ /if }}
226
+ {{ /echo }}
227
+ ```
228
+
229
+ Rather than having to do multiple `{{ /echo }} ... {{ echo }}` statements, the previous can be rewritten as such:
230
+
231
+ ```nunjucks
232
+ <!-- This: -->
233
+ {{! if someCondition }}
234
+ <p>{{! getServerData }}</p>
235
+
236
+ <p>Page built on: {{ localBuildTime }}</p>
237
+ {{! /if }}
238
+
239
+ <!-- Renders as: -->
240
+ {{ if someCondition }}
241
+ <p>{{ getServerData }}</p>
242
+
243
+ <p>Page built on: 04-08-2024</p>
244
+ {{ /if }}
245
+ ```
package/release.config.js CHANGED
@@ -2,8 +2,9 @@ const presetConfig = {
2
2
  types: [
3
3
  { type: 'feat', section: 'New Features' },
4
4
  { type: 'fix', section: 'Bug Fixes' },
5
- { type: 'refactor', section: 'Refactorings' },
6
- { type: 'docs', section: 'Documentation' },
5
+ { type: 'refactor', section: 'Refactors' },
6
+ { type: 'upstream', section: 'Dependency Updates' },
7
+ { type: 'docs', hidden: true },
7
8
  { type: 'chore', hidden: true },
8
9
  { type: 'perf', hidden: true },
9
10
  { type: 'style', hidden: true },
@@ -11,10 +12,16 @@ const presetConfig = {
11
12
  ],
12
13
  };
13
14
 
15
+ const releaseRules = [
16
+ { breaking: true, release: 'major' },
17
+ { type: 'upstream', release: 'minor' },
18
+ ];
19
+
14
20
  /** @type {import('semantic-release').Options} */
15
21
  export default {
16
22
  preset: 'conventionalcommits',
17
23
  presetConfig,
24
+ releaseRules,
18
25
  plugins: [
19
26
  ['@semantic-release/commit-analyzer'],
20
27
  '@semantic-release/release-notes-generator',
@@ -1,5 +1,5 @@
1
1
  import { expect, test } from 'vitest';
2
- import { runBuild } from '../get-instance.js';
2
+ import { runBuild } from '#test-instance';
3
3
 
4
4
  const [trimSingle, trimAll] = await Promise.all([
5
5
  runBuild(import.meta.dirname, { vento: { trimTags: ['set'] } }),
@@ -8,9 +8,7 @@ const [trimSingle, trimAll] = await Promise.all([
8
8
 
9
9
  test('should trim a single tag', async () => {
10
10
  const page = trimSingle[0];
11
- await expect(page.content).toMatchFileSnapshot(
12
- './build/autotrim-single.html'
13
- );
11
+ await expect(page.content).toMatchFileSnapshot('./build/autotrim-single.html');
14
12
  });
15
13
 
16
14
  test('should trim all tags', async () => {
@@ -1,11 +1,9 @@
1
1
  import { expect, test } from 'vitest';
2
- import { runBuild } from '../get-instance.js';
2
+ import { runBuild } from '#test-instance';
3
3
 
4
4
  const results = await runBuild(import.meta.dirname);
5
5
 
6
6
  test('renders a basic template', async () => {
7
7
  const page = results[0];
8
- await expect(page.content).toMatchFileSnapshot(
9
- './__snapshots__/template-basic.html'
10
- );
8
+ await expect(page.content).toMatchFileSnapshot('./__snapshots__/template-basic.html');
11
9
  });
@@ -1,5 +1,5 @@
1
1
  import { expect, test } from 'vitest';
2
- import { runBuild } from '../get-instance.js';
2
+ import { runBuild } from '#test-instance';
3
3
 
4
4
  const results = await runBuild(import.meta.dirname, {
5
5
  eleventy(configApi) {
@@ -9,10 +9,7 @@ const results = await runBuild(import.meta.dirname, {
9
9
  (content) => content.toUpperCase()
10
10
  );
11
11
 
12
- configApi.addFilter(
13
- 'wrapWith',
14
- (content, tag) => `<${tag}>${content}</${tag}>`
15
- );
12
+ configApi.addFilter('wrapWith', (content, tag) => `<${tag}>${content}</${tag}>`);
16
13
 
17
14
  configApi.addFilter('pageUrlCompare', function (url) {
18
15
  return url === this.page.url;
@@ -22,16 +19,12 @@ const results = await runBuild(import.meta.dirname, {
22
19
 
23
20
  test('transforms string with built-in method', async () => {
24
21
  const page = results.find(({ url }) => url === '/basic/');
25
- await expect(page.content).toMatchFileSnapshot(
26
- './__snapshots__/filters-basic.html'
27
- );
22
+ await expect(page.content).toMatchFileSnapshot('./__snapshots__/filters-basic.html');
28
23
  });
29
24
 
30
25
  test('transforms string with passed arguments', async () => {
31
26
  const page = results.find(({ url }) => url === '/with-arguments/');
32
- await expect(page.content).toMatchFileSnapshot(
33
- './__snapshots__/filters-arguments.html'
34
- );
27
+ await expect(page.content).toMatchFileSnapshot('./__snapshots__/filters-arguments.html');
35
28
  });
36
29
 
37
30
  test('verify page context in scoped data', async () => {
@@ -1,20 +1,14 @@
1
1
  import { expect, test } from 'vitest';
2
- import { runBuild } from '../get-instance.js';
3
-
4
- console.log(import.meta.dirname);
2
+ import { runBuild } from '#test-instance';
5
3
 
6
4
  const results = await runBuild(import.meta.dirname);
7
5
 
8
6
  test('include a page', async () => {
9
7
  const page = results.find(({ url }) => url === '/basic/');
10
- await expect(page.content).toMatchFileSnapshot(
11
- './__snapshots__/includes-basic.html'
12
- );
8
+ await expect(page.content).toMatchFileSnapshot('./__snapshots__/includes-basic.html');
13
9
  });
14
10
 
15
11
  test('include a page with data passing', async () => {
16
12
  const page = results.find(({ url }) => url === '/with-data/');
17
- await expect(page.content).toMatchFileSnapshot(
18
- './__snapshots__/includes-data.html'
19
- );
13
+ await expect(page.content).toMatchFileSnapshot('./__snapshots__/includes-data.html');
20
14
  });
@@ -1,18 +1,14 @@
1
1
  import { expect, test } from 'vitest';
2
- import { runBuild } from '../get-instance.js';
2
+ import { runBuild } from '#test-instance';
3
3
 
4
4
  const results = await runBuild(import.meta.dirname);
5
5
 
6
6
  test('render page with example layout 1', async () => {
7
7
  const page = results.find(({ url }) => url === '/page1/');
8
- await expect(page.content).toMatchFileSnapshot(
9
- './__snapshots__/example-layout-1.html'
10
- );
8
+ await expect(page.content).toMatchFileSnapshot('./__snapshots__/example-layout-1.html');
11
9
  });
12
10
 
13
11
  test('render page with example layout 2', async () => {
14
12
  const page = results.find(({ url }) => url === '/page2/');
15
- await expect(page.content).toMatchFileSnapshot(
16
- './__snapshots__/example-layout-2.html'
17
- );
13
+ await expect(page.content).toMatchFileSnapshot('./__snapshots__/example-layout-2.html');
18
14
  });
@@ -1,5 +1,5 @@
1
1
  import { expect, test } from 'vitest';
2
- import { runBuild } from '../get-instance.js';
2
+ import { runBuild } from '#test-instance';
3
3
 
4
4
  const results = await runBuild(import.meta.dirname);
5
5
 
@@ -1,24 +1,19 @@
1
- import { test } from 'vitest';
2
- import { runBuild } from '../get-instance.js';
1
+ import { expect, test } from 'vitest';
2
+ import { runBuild } from '#test-instance';
3
3
 
4
4
  const results = await runBuild(import.meta.dirname, {
5
5
  eleventy(configApi) {
6
6
  configApi.addShortcode('helloWorld', () => 'Hello world!');
7
- configApi.addShortcode(
8
- 'possumPosse',
9
- (number = 'the') => `Release ${number} possums!!!`
10
- );
7
+ configApi.addShortcode('possumPosse', (number = 'the') => `Release ${number} possums!!!`);
11
8
  },
12
9
  });
13
10
 
14
- test.concurrent('shortcode without argument', ({ expect }) => {
11
+ test('shortcode without argument', () => {
15
12
  const page = results.find(({ url }) => url === '/basic/');
16
13
  expect(page.content).toBe('Hello world!\n');
17
14
  });
18
15
 
19
- test.concurrent('shortcode with arguments', async ({ expect }) => {
16
+ test('shortcode with arguments', async () => {
20
17
  const page = results.find(({ url }) => url === '/with-arguments/');
21
- await expect(page.content).toMatchFileSnapshot(
22
- './__snapshots__/shortcode-with-arguments.html'
23
- );
18
+ await expect(page.content).toMatchFileSnapshot('./__snapshots__/shortcode-with-arguments.html');
24
19
  });
@@ -0,0 +1 @@
1
+ <p>{{ dynamicValue }}</p>
@@ -0,0 +1,7 @@
1
+ {{ echo }}
2
+
3
+ {{ dataValue }}
4
+
5
+ <p>Some more {{ dataValue }}</p>
6
+
7
+ {{ /echo }}
@@ -0,0 +1 @@
1
+ {{> console.log('hello world!') }}
@@ -0,0 +1,3 @@
1
+ {{ for value of [1,2,3] }}
2
+ {{ value }}
3
+ {{ /for }}
@@ -0,0 +1,5 @@
1
+ {{ if true }}
2
+
3
+ <p>Some content</p>
4
+
5
+ {{ /if }}
@@ -0,0 +1 @@
1
+ <p>{{! dynamicValue }}</p>
@@ -0,0 +1,7 @@
1
+ {{! echo }}
2
+
3
+ {{! dataValue }}
4
+
5
+ <p>Some more {{! dataValue }}</p>
6
+
7
+ {{! /echo }}
@@ -0,0 +1 @@
1
+ {{!> console.log('hello world!') }}
@@ -0,0 +1,3 @@
1
+ {{! for value of [1,2,3] }}
2
+ {{! value }}
3
+ {{! /for }}
@@ -0,0 +1,5 @@
1
+ {{! if true }}
2
+
3
+ <p>Some content</p>
4
+
5
+ {{! /if }}
@@ -0,0 +1,29 @@
1
+ import { expect, test } from 'vitest';
2
+ import { runBuild } from '#test-instance';
3
+
4
+ const results = await runBuild(import.meta.dirname);
5
+
6
+ test('render if', async () => {
7
+ const page = results.find(({ url }) => url === '/if/');
8
+ await expect(page.content).toMatchFileSnapshot('./__snapshots__/ssr-if.html');
9
+ });
10
+
11
+ test('render data', async () => {
12
+ const page = results.find(({ url }) => url === '/data/');
13
+ await expect(page.content).toMatchFileSnapshot('./__snapshots__/ssr-data.html');
14
+ });
15
+
16
+ test('render for', async () => {
17
+ const page = results.find(({ url }) => url === '/for/');
18
+ await expect(page.content).toMatchFileSnapshot('./__snapshots__/ssr-for.html');
19
+ });
20
+
21
+ test('render echo', async () => {
22
+ const page = results.find(({ url }) => url === '/echo/');
23
+ await expect(page.content).toMatchFileSnapshot('./__snapshots__/ssr-echo.html');
24
+ });
25
+
26
+ test('render exec', async () => {
27
+ const page = results.find(({ url }) => url === '/exec/');
28
+ await expect(page.content).toMatchFileSnapshot('./__snapshots__/ssr-exec.html');
29
+ });