eleventy-plugin-vento 3.1.0 → 3.2.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/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # eleventy-plugin-vento
2
2
 
3
+ ## 3.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - f9388e1: Further refine caching between Eleventy and Vento: commit ec02a765 deferred **all** caching to Vento and turned off Eleventy's cache (released in 3.1.0). This release allows Vento to cache only the templates that Eleventy can't cache (permalinks, includes, etc.), and Eleventy caches everything else.
8
+
9
+ A performance oversight has also been resolved by this change. Now, template functions are pre-compiled and saved by Eleventy, only running the template on render without having to go through a re-compile or get a template from the cache.
10
+
11
+ ### Patch Changes
12
+
13
+ - 693e1a2: Refactored the following features internally:
14
+
15
+ - Reverts 2c3548c1 for modularity: The compatibility check has moved back to `modules/utils.js`
16
+ - Revises caching further: Instead of deferring to Vento for everything, permalink templates and templates loaded by Vento (like includes) are cached by Vento. Everything else is cached by Eleventy.
17
+ - Rename some internal variables to help with readability (`_11ty.ctx => _11Ctx`)
18
+ - `DEBUG` key `Eleventy:Vento:Template` renamed to `Eleventy:Vento:Render`
19
+ - Declare engine functions separately and return as an object after everything has been declared.
20
+
3
21
  ## 3.1.0
4
22
 
5
23
  ### Minor Changes
package/package.json CHANGED
@@ -1,14 +1,11 @@
1
1
  {
2
2
  "name": "eleventy-plugin-vento",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
4
  "description": "Adds support for the Vento templating language to Eleventy.",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": ">=16"
8
8
  },
9
- "imports": {
10
- "#11ty-test": "./tests/_utils/eleventy-test.js"
11
- },
12
9
  "exports": {
13
10
  ".": {
14
11
  "import": "./src/index.js"
@@ -47,7 +44,7 @@
47
44
  "eslint-config-prettier": "9.1.0",
48
45
  "eslint-plugin-unicorn": "56.0.0",
49
46
  "globals": "15.11.0",
50
- "vitest": "2.1.3"
47
+ "vitest": "2.1.4"
51
48
  },
52
49
  "prettier": {
53
50
  "bracketSameLine": true,
@@ -56,7 +53,7 @@
56
53
  "trailingComma": "es5"
57
54
  },
58
55
  "scripts": {
59
- "test": "vitest --run",
56
+ "test": "vitest",
60
57
  "lint": "eslint '**/*.js'",
61
58
  "ci:version": "changeset version && pnpm install --no-frozen-lockfile",
62
59
  "ci:publish": "changeset publish",
package/readme.md CHANGED
@@ -17,6 +17,7 @@ An [Eleventy](https://11ty.dev/) plugin that adds support for [Vento](https://ve
17
17
  [Vento Plugins](#vento-plugins)<br>
18
18
  [Auto-Trimming Tags](#auto-trimming-tags)<br>
19
19
  [Ignoring Tags](#ignoring-tags)<br>
20
+ [Debugging](#debugging)<br>
20
21
 
21
22
  ## Installing
22
23
 
@@ -28,7 +29,7 @@ npm install eleventy-plugin-vento
28
29
 
29
30
  This plugin is ESM only and cannot be required from CommonJS.
30
31
 
31
- If you're using CommonJS and loading it asynchronously (ie `await import`), you will need at **minimum** Eleventy v3.0.0-alpha.15 which provides internal methods this plugin uses to get Eleventy stuff from the config API.
32
+ If you're using CommonJS and loading it asynchronously (ie `await import`), you will need at **minimum** Eleventy v3.0.0-alpha.15 which provides internal methods this plugin uses to retrieve Eleventy features from the config API.
32
33
 
33
34
  In your Eleventy config:
34
35
 
@@ -101,7 +102,7 @@ Eleventy: See [Filters](https://www.11ty.dev/docs/filters/)
101
102
  > [!NOTE]
102
103
  > Remember, Vento can print any return value from a Javascript expression, as well as [run arbitrary JavaScript](https://vento.js.org/syntax/javascript/) in templates through its `{{> ...}}` operator. In these cases, shortcodes may not be needed depending on your usage.
103
104
 
104
- Single and Paired Shortcodes added via Eleventy's `.addShortcode()`, `addAsyncShortcode()`, `addPairedShortcode()` or `.addAsyncPairedShortcode()` will be automatically loaded into Vento.
105
+ Single and Paired Shortcodes added via Eleventy's `.addShortcode()`, `.addAsyncShortcode()`, `.addPairedShortcode()` or `.addAsyncPairedShortcode()` will be automatically loaded into Vento.
105
106
 
106
107
  When using shortcodes in your templates, write them like any other Vento tag:
107
108
 
@@ -272,7 +273,7 @@ Like Eleventy, this plugin uses the [`debug`](https://www.npmjs.com/package/debu
272
273
 
273
274
  - `Eleventy:Vento:Setup` - Logs initial setup of the plugin, loading features, pre-page compile setup steps (like changing `page` and `eleventy` objects)
274
275
  - `Eleventy:Vento:Cache` - Logs updates to Vento's own internal cache, which is used in tandem with Eleventy's cache.
275
- - `Eleventy:Vento:Template` - Logs rendered templates and other template related actions
276
+ - `Eleventy:Vento:Render` - Logs when templates are rendered
276
277
 
277
278
  Because it is a child of the `Eleventy:` namespace, the following command will include output from this plugin as well:
278
279
 
package/src/engine.js CHANGED
@@ -3,11 +3,15 @@
3
3
  * a simple API for Eleventy to interface with.
4
4
  *
5
5
  * @typedef {{eleventy?: Record<string, unknown>, page?: Record<string, unknown>}} EleventyContext
6
- * @typedef {Context & Record<string, unknown>} EleventyData
6
+ * @typedef {EleventyContext & Record<string, unknown>} EleventyData
7
7
  * @typedef {(...args: unknown[]) => unknown} EleventyFunction
8
+ * @typedef {Record<string, EleventyFunction>} EleventyFunctionSet
8
9
  * @typedef {import('ventojs/src/environment.js').Environment} VentoEnvironment
9
10
  * @typedef {VentoEnvironment & {
10
- * utils: { _11ty: { ctx: Context, tags: Record<string, EleventyFunction> } }
11
+ * utils: {
12
+ * _11tyFns: { shortcodes: EleventyFunctionSet, pairedShortcodes: EleventyFunctionSet }
13
+ * _11tyCtx: EleventyContext
14
+ * }
11
15
  * }} EleventyVentoEnvironment
12
16
  */
13
17
 
@@ -16,80 +20,107 @@ import ventojs from 'ventojs';
16
20
 
17
21
  // Internal modules
18
22
  import { createVentoTag } from './modules/create-vento-tag.js';
19
- import { DEBUG, CONTEXT_DATA_KEYS } from './modules/utils.js';
23
+ import { CONTEXT_DATA_KEYS, DEBUG } from './modules/utils.js';
20
24
 
21
25
  /** @param {import('ventojs').Options} options */
22
26
  export function createVentoEngine(options) {
23
27
  /** @type {EleventyVentoEnvironment} */
24
28
  const env = ventojs(options);
25
- env.utils._11ty = { ctx: {}, shortcodes: {}, pairedShortcodes: {} };
29
+ env.utils._11tyFns = { shortcodes: {}, pairedShortcodes: {} };
30
+ env.utils._11tyCtx = {};
26
31
 
27
- return {
28
- cache: env.cache,
32
+ /** @param {EleventyData} newContext */
33
+ function setContext(newContext) {
34
+ if (env.utils._11tyCtx?.page?.inputPath === newContext?.page?.inputPath) {
35
+ return;
36
+ }
29
37
 
30
- /** @param {import('ventojs/src/environment.js').Plugin[]} plugins */
31
- loadPlugins(plugins) {
32
- for (const plugin of plugins) {
33
- env.use(plugin);
34
- }
35
- },
38
+ for (const K of CONTEXT_DATA_KEYS) {
39
+ env.utils._11tyCtx[K] = newContext[K];
40
+ }
36
41
 
37
- /** @param {PageData} newContext */
38
- loadContext(newContext) {
39
- // Loop through allowed keys and load those into the context
40
- for (const K of CONTEXT_DATA_KEYS) {
41
- env.utils._11ty.ctx[K] = newContext[K];
42
- }
43
- },
42
+ DEBUG.setup('Reload context, new context is: %o', env.utils._11tyCtx);
43
+ }
44
44
 
45
- /** @param {Record<string, EleventyFunction>} filters */
46
- loadFilters(filters) {
47
- for (const [name, fn] of Object.entries(filters)) {
48
- env.filters[name] = (...args) => fn.apply(env.utils._11ty.ctx, args);
49
- }
50
- },
45
+ /** @param {import('ventojs/src/environment.js').Plugin[]} plugins */
46
+ function loadPlugins(plugins) {
47
+ for (const plugin of plugins) {
48
+ env.use(plugin);
49
+ }
50
+ }
51
51
 
52
- /** @param {Record<string, EleventyFunction>} shortcodes */
53
- loadShortcodes(shortcodes) {
54
- for (const [name, fn] of Object.entries(shortcodes)) {
55
- env.utils._11ty.shortcodes[name] = fn;
56
- env.tags.push(createVentoTag({ name, group: 'shortcodes' }));
57
- }
58
- },
52
+ /** @param {Record<string, EleventyFunction>} filters */
53
+ function loadFilters(filters) {
54
+ for (const [name, fn] of Object.entries(filters)) {
55
+ env.filters[name] = fn.bind(env.utils._11tyCtx);
56
+ }
57
+ }
59
58
 
60
- /** @param {Record<string, EleventyFunction>} pairedShortcodes */
61
- loadPairedShortcodes(pairedShortcodes) {
62
- for (const [name, fn] of Object.entries(pairedShortcodes)) {
63
- env.utils._11ty.pairedShortcodes[name] = fn;
64
- env.tags.push(createVentoTag({ name, group: 'pairedShortcodes' }));
65
- }
66
- },
67
-
68
- /** @param {{ source: string, data: PageData, file?: string }} input */
69
- async process(input) {
70
- // Reload context
71
- this.loadContext(input.data);
72
- DEBUG.setup('Reloaded Eleventy/Vento context. New value: %o', env.utils._11ty.ctx);
73
-
74
- // Retrieve the template from the cache
75
- let template = env.cache.get(input.file);
76
-
77
- if (template?.source === input.source) {
78
- DEBUG.cache('Cache HIT for `%s`, used precompiled template', input.file);
79
- } else {
80
- template = env.compile(input.source, input.file);
81
-
82
- if (input.file) {
83
- DEBUG.cache('Cache MISS for `%s`, compiled new template', input.file);
84
- env.cache.set(input.file, template);
85
- }
59
+ /** @param {Record<string, EleventyFunction>} shortcodes */
60
+ function loadShortcodes(shortcodes) {
61
+ for (const [name, fn] of Object.entries(shortcodes)) {
62
+ env.utils._11tyFns.shortcodes[name] = fn;
63
+ env.tags.push(createVentoTag({ name, group: 'shortcodes' }));
64
+ }
65
+ }
66
+
67
+ /** @param {Record<string, EleventyFunction>} pairedShortcodes */
68
+ function loadPairedShortcodes(pairedShortcodes) {
69
+ for (const [name, fn] of Object.entries(pairedShortcodes)) {
70
+ env.utils._11tyFns.pairedShortcodes[name] = fn;
71
+ env.tags.push(createVentoTag({ name, group: 'pairedShortcodes' }));
72
+ }
73
+ }
74
+
75
+ /**
76
+ * @param {string} source
77
+ * @param {string} file
78
+ * @param {boolean} [useVentoCache=true]
79
+ */
80
+ function getTemplateFunction(source, file, useVentoCache = true) {
81
+ // Attempt to retrieve template function from cache
82
+ let template = env.cache.get(file);
83
+
84
+ if (template?.source === source) {
85
+ DEBUG.cache('Cache HIT for `%s`, used precompiled template', file);
86
+ } else {
87
+ template = env.compile(source, file);
88
+
89
+ DEBUG.cache(
90
+ `Cache MISS for \`%s\`, compiled new template:\nUse Vento cache: %o\n\n${template}`,
91
+ file,
92
+ useVentoCache
93
+ );
94
+ if (useVentoCache) {
95
+ env.cache.set(file, template);
86
96
  }
97
+ }
98
+
99
+ return template;
100
+ }
87
101
 
88
- // Run the template
89
- DEBUG.template(template.code);
90
- const { content } = await template(input.data);
102
+ /**
103
+ * @param {import('ventojs/src/environment.js').Template} template
104
+ * @param {EleventyData} data
105
+ * @param {string} from
106
+ */
107
+ async function render(template, data, from) {
108
+ // Load new context
109
+ setContext(data);
91
110
 
92
- return content;
93
- },
111
+ // Render template
112
+ DEBUG.render('Rendering `%s`', from);
113
+ const { content } = await template(data);
114
+ return content;
115
+ }
116
+
117
+ return {
118
+ cache: env.cache,
119
+ loadPlugins,
120
+ loadFilters,
121
+ loadShortcodes,
122
+ loadPairedShortcodes,
123
+ getTemplateFunction,
124
+ render,
94
125
  };
95
126
  }
package/src/index.js CHANGED
@@ -29,7 +29,7 @@ import autotrimPlugin, { defaultTags as autotrimDefaultTags } from 'ventojs/plug
29
29
  // Local modules
30
30
  import { createVentoEngine } from './engine.js';
31
31
  import { ignoreTagPlugin } from './modules/ignore-tag.js';
32
- import { DEBUG, PERMALINK_PREFIX, REQUIRED_API_METHODS } from './modules/utils.js';
32
+ import { DEBUG, runCompatibilityCheck } from './modules/utils.js';
33
33
 
34
34
  /**
35
35
  * @param {import('@11ty/eleventy').UserConfig} eleventyConfig
@@ -37,18 +37,7 @@ import { DEBUG, PERMALINK_PREFIX, REQUIRED_API_METHODS } from './modules/utils.j
37
37
  */
38
38
  export function VentoPlugin(eleventyConfig, userOptions) {
39
39
  DEBUG.setup('Initializing eleventy-plugin-vento');
40
- DEBUG.setup('Run compatibility check');
41
- for (const [method, version] of REQUIRED_API_METHODS) {
42
- if (!eleventyConfig?.[method]) {
43
- console.error(
44
- '[eleventy-plugin-vento] Plugin compatibility error:',
45
- `\`${method}\``,
46
- 'not found. Please use Eleventy',
47
- version,
48
- 'or later.'
49
- );
50
- }
51
- }
40
+ runCompatibilityCheck(eleventyConfig);
52
41
 
53
42
  /** @type {VentoPluginOptions} */
54
43
  const options = {
@@ -153,26 +142,36 @@ export function VentoPlugin(eleventyConfig, userOptions) {
153
142
  outputFileExtension: 'html',
154
143
  read: true,
155
144
 
156
- // Main compile function
157
- async compile(source, file) {
158
- file = path.normalize(file);
159
- return async (data) => await engine.process({ source, data, file });
145
+ compile(inputContent, inputPath) {
146
+ // Normalize input path
147
+ inputPath = path.normalize(inputPath);
148
+
149
+ // Retrieve the template function
150
+ const template = engine.getTemplateFunction(inputContent, inputPath, false);
151
+
152
+ // Return a render function
153
+ return async (data) => await engine.render(template, data, inputPath);
160
154
  },
161
155
 
162
156
  compileOptions: {
163
- // Defer all caching to Vento
164
- cache: false,
165
157
  // Custom permalink compilation
166
- permalink(source, file) {
158
+ permalink(permalinkContent, inputPath) {
167
159
  // Short circuit if input isn't a string and doesn't look like a vento template
168
- if (typeof source === 'string' && /\{\{\s+.+\s+\}\}/.test(source)) {
169
- file = PERMALINK_PREFIX + path.normalize(file);
170
- return async (data) => engine.process({ source, data, file });
160
+ if (typeof permalinkContent === 'string' && /\{\{\s+.+\s+\}\}/.test(permalinkContent)) {
161
+ // Normalize input path
162
+ inputPath = 'Permalink::' + path.normalize(inputPath);
163
+
164
+ // Retrieve the template function
165
+ const template = engine.getTemplateFunction(permalinkContent, inputPath);
166
+
167
+ // Return a render function
168
+ return async (data) => await engine.render(template, data, inputPath);
171
169
  }
172
170
 
173
- return source;
171
+ return permalinkContent;
174
172
  },
175
173
  },
176
174
  });
175
+
177
176
  DEBUG.setup('eleventy-plugin-vento initialized');
178
177
  }
@@ -12,8 +12,8 @@ export function createVentoTag(options) {
12
12
  if (!code.startsWith(options.name)) return;
13
13
 
14
14
  // Declare helper variables for repeated strings in template
15
- const fn = `__env.utils._11ty.${options.group}.${options.name}`;
16
- const ctx = '__env.utils._11ty.ctx';
15
+ const fn = `__env.utils._11tyFns.${options.group}.${options.name}`;
16
+ const ctx = '__env.utils._11tyCtx';
17
17
  const args = [code.replace(options.name, '').trim()];
18
18
 
19
19
  const varname = output.startsWith('__shortcode_content')
@@ -4,7 +4,7 @@ const debugBaseNamespace = 'Eleventy:Vento';
4
4
  export const DEBUG = {
5
5
  setup: createDebugger(`${debugBaseNamespace}:Setup`),
6
6
  cache: createDebugger(`${debugBaseNamespace}:Cache`),
7
- template: createDebugger(`${debugBaseNamespace}:Template`),
7
+ render: createDebugger(`${debugBaseNamespace}:Render`),
8
8
  };
9
9
 
10
10
  // Project-wide constants
@@ -17,4 +17,18 @@ export const REQUIRED_API_METHODS = [
17
17
 
18
18
  export const CONTEXT_DATA_KEYS = ['page', 'eleventy'];
19
19
 
20
- export const PERMALINK_PREFIX = 'EleventyVentoPermalink:';
20
+ // Helper functions
21
+ export function runCompatibilityCheck(config) {
22
+ DEBUG.setup('Run compatibility check');
23
+ for (const [method, version] of REQUIRED_API_METHODS) {
24
+ if (!config?.[method]) {
25
+ console.error(
26
+ '[eleventy-plugin-vento] Plugin compatibility error:',
27
+ `\`${method}\``,
28
+ 'not found. Please use Eleventy',
29
+ version,
30
+ 'or later.'
31
+ );
32
+ }
33
+ }
34
+ }