eleventy-plugin-vento 3.0.2 → 3.1.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,54 @@
1
1
  # eleventy-plugin-vento
2
2
 
3
+ ## 3.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 7e6ba68: Separate `shortcodes` and `pairedShortcodes` into seperate object namespaces. Prior to this version, Eleventy shortcodes and paired shortcodes were merged into a single object keyed as `_11ty.functions` which allowed for naming collisions between shortcodes and their paired counterparts.
8
+ - b8f0a03: Adds new dependency on `debug` package, to help out with testing and getting more verbose logs. The following `DEBUG` namespaces are implemented:
9
+
10
+ - `Eleventy:Vento:Setup` - Logs initial setup of the plugin, loading features, pre-page compile setup steps (like changing `page` and `eleventy` objects)
11
+ - `Eleventy:Vento:Cache` - Logs updates to Vento's own internal cache, which is used in tandem with Eleventy's cache.
12
+ - `Eleventy:Vento:Template` - Logs rendered templates and other template related actions
13
+
14
+ Because it is a child of the `Eleventy:` namespace, the following command will include output from this plugin as well:
15
+
16
+ ```sh
17
+ $ DEBUG='Eleventy:*' npx @11ty/eleventy
18
+ ```
19
+
20
+ Alternatively, use a finer grained namespace to see only the output generated by `eleventy-plugin-vento`.
21
+
22
+ ```sh
23
+ $ DEBUG='Eleventy:Vento:*' npx @11ty/eleventy
24
+ ```
25
+
26
+ ```sh
27
+ $ DEBUG='Eleventy:Vento:Template' npx @11ty/eleventy
28
+ ```
29
+
30
+ See [the docs on 11ty.dev](https://www.11ty.dev/docs/debugging/) as well as the [debug package README](https://github.com/debug-js/debug/blob/master/README.md) for more information.
31
+
32
+ - ec02a76: Further caching improvements to e8111234 and e46ce6ea. Eleventy now defers all template caching to Vento.
33
+
34
+ Templates are now compiled directly instead of using Vento's `.runString` method which reduces overhead and enables this plugin to manage the Vento cache in a more direct manner.
35
+
36
+ - 494b184: Permalink compilation optimizations:
37
+
38
+ - Permalinks now short-circuit to raw strings if they don't contain Vento template syntax, avoiding compilation entirely.
39
+ - Permalinks are now assigned (fake) pathnames in Vento's cache. If you change template content but don't change a dynamic permalink in development, Vento will reuse the compiled permalink template. Note that **any** modifications to a template file will **always** cause it to be recompiled (see 19c352fa) — this improvement just optimizes recompiling dynamic permalinks that haven't changed.
40
+
41
+ ### Patch Changes
42
+
43
+ - 4b4b387: Entries for changed files are now deleted from Vento's cache when running the Eleventy Dev server. (fixes #38)
44
+
45
+ ## 3.0.3-next.0
46
+
47
+ ### Patch Changes
48
+
49
+ - e811123: Partially reverts e46ce6ea, refine caching logic. Pre-compile, the plugin checks the Vento cache to see what source needs compiling and if that matches what was already compiled. If not, then the cache is cleared so Vento can recompile.
50
+ - 2f32efb: Clear Vento cache on every template change, to help with #38. Resolution is still WIP.
51
+
3
52
  ## 3.0.2
4
53
 
5
54
  ### Patch Changes
package/package.json CHANGED
@@ -1,11 +1,14 @@
1
1
  {
2
2
  "name": "eleventy-plugin-vento",
3
- "version": "3.0.2",
3
+ "version": "3.1.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
+ },
9
12
  "exports": {
10
13
  ".": {
11
14
  "import": "./src/index.js"
@@ -32,18 +35,19 @@
32
35
  "CHANGELOG.md"
33
36
  ],
34
37
  "dependencies": {
38
+ "debug": "4.3.7",
35
39
  "ventojs": "1.12.10"
36
40
  },
37
41
  "devDependencies": {
38
42
  "@11ty/eleventy": "3.0.0",
39
43
  "@changesets/cli": "2.27.9",
40
- "eslint": "9.12.0",
44
+ "@eslint/js": "9.13.0",
45
+ "eslint": "9.13.0",
41
46
  "eslint-config-flat-gitignore": "0.3.0",
42
47
  "eslint-config-prettier": "9.1.0",
43
48
  "eslint-plugin-unicorn": "56.0.0",
44
49
  "globals": "15.11.0",
45
- "vitest": "2.1.2",
46
- "zx": "8.1.9"
50
+ "vitest": "2.1.3"
47
51
  },
48
52
  "prettier": {
49
53
  "bracketSameLine": true,
package/readme.md CHANGED
@@ -265,3 +265,33 @@ Rather than having to do multiple `{{ /echo }} ... {{ echo }}` statements, the p
265
265
  <p>Page built on: 09-19-2024</p>
266
266
  {{ /if }}
267
267
  ```
268
+
269
+ ## Debugging
270
+
271
+ Like Eleventy, this plugin uses the [`debug`](https://www.npmjs.com/package/debug) package to handle verbose logging. The following `DEBUG` namespaces are implemented:
272
+
273
+ - `Eleventy:Vento:Setup` - Logs initial setup of the plugin, loading features, pre-page compile setup steps (like changing `page` and `eleventy` objects)
274
+ - `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
+
277
+ Because it is a child of the `Eleventy:` namespace, the following command will include output from this plugin as well:
278
+
279
+ ```sh
280
+ $ DEBUG='Eleventy:*' npx @11ty/eleventy
281
+ ```
282
+
283
+ Alternatively, use a finer grained namespace to see only the output generated by `eleventy-plugin-vento`.
284
+
285
+ ```sh
286
+ $ DEBUG='Eleventy:Vento:*' npx @11ty/eleventy
287
+ ```
288
+
289
+ ```sh
290
+ $ DEBUG='Eleventy:Vento:Template' npx @11ty/eleventy
291
+ ```
292
+
293
+ ### Relevant Documentation
294
+
295
+ Eleventy: See [Debug Mode](https://www.11ty.dev/docs/debugging/).
296
+
297
+ Debug: See [`debug` on npm](https://www.npmjs.com/package/debug).
package/src/engine.js CHANGED
@@ -16,24 +16,16 @@ import ventojs from 'ventojs';
16
16
 
17
17
  // Internal modules
18
18
  import { createVentoTag } from './modules/create-vento-tag.js';
19
- import { CONTEXT_DATA_KEYS } from './modules/utils.js';
19
+ import { DEBUG, CONTEXT_DATA_KEYS } from './modules/utils.js';
20
20
 
21
21
  /** @param {import('ventojs').Options} options */
22
22
  export function createVentoEngine(options) {
23
23
  /** @type {EleventyVentoEnvironment} */
24
24
  const env = ventojs(options);
25
- env.utils._11ty = { ctx: {}, functions: {} };
25
+ env.utils._11ty = { ctx: {}, shortcodes: {}, pairedShortcodes: {} };
26
26
 
27
27
  return {
28
- /** @param {string} path */
29
- getCachedSource(path) {
30
- return env.cache.get(path)?.source;
31
- },
32
-
33
- /** @param {string} path */
34
- emptyCache(path) {
35
- return path ? env.cache.delete(path) : env.cache.clear();
36
- },
28
+ cache: env.cache,
37
29
 
38
30
  /** @param {import('ventojs/src/environment.js').Plugin[]} plugins */
39
31
  loadPlugins(plugins) {
@@ -57,26 +49,47 @@ export function createVentoEngine(options) {
57
49
  }
58
50
  },
59
51
 
60
- /**
61
- * @param {Record<string, EleventyFunction>} shortcodes
62
- * @param {boolean} paired
63
- */
64
- loadShortcodes(shortcodes, paired = false) {
52
+ /** @param {Record<string, EleventyFunction>} shortcodes */
53
+ loadShortcodes(shortcodes) {
65
54
  for (const [name, fn] of Object.entries(shortcodes)) {
66
- env.utils._11ty.functions[name] = fn;
67
- env.tags.push(createVentoTag(name, paired));
55
+ env.utils._11ty.shortcodes[name] = fn;
56
+ env.tags.push(createVentoTag({ name, group: 'shortcodes' }));
57
+ }
58
+ },
59
+
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' }));
68
65
  }
69
66
  },
70
67
 
71
- /** @param {{data: PageData, source: string, path: string}} input */
68
+ /** @param {{ source: string, data: PageData, file?: string }} input */
72
69
  async process(input) {
73
70
  // Reload context
74
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
+ }
86
+ }
75
87
 
76
- // Process the templates
77
- const result = await env.runString(input.source, input.data, input.path);
88
+ // Run the template
89
+ DEBUG.template(template.code);
90
+ const { content } = await template(input.data);
78
91
 
79
- return result.content;
92
+ return content;
80
93
  },
81
94
  };
82
95
  }
package/src/index.js CHANGED
@@ -20,20 +20,35 @@
20
20
  * (See [Vento Docs](https://vento.js.org/configuration/#options))
21
21
  */
22
22
 
23
+ // Built-ins
24
+ import path from 'node:path';
25
+
23
26
  // External modules
24
27
  import autotrimPlugin, { defaultTags as autotrimDefaultTags } from 'ventojs/plugins/auto_trim.js';
25
28
 
26
29
  // Local modules
27
30
  import { createVentoEngine } from './engine.js';
28
31
  import { ignoreTagPlugin } from './modules/ignore-tag.js';
29
- import { runCompatibilityCheck } from './modules/utils.js';
32
+ import { DEBUG, PERMALINK_PREFIX, REQUIRED_API_METHODS } from './modules/utils.js';
30
33
 
31
34
  /**
32
35
  * @param {import('@11ty/eleventy').UserConfig} eleventyConfig
33
36
  * @param {Partial<VentoPluginOptions>} userOptions
34
37
  */
35
38
  export function VentoPlugin(eleventyConfig, userOptions) {
36
- runCompatibilityCheck(eleventyConfig);
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
+ }
37
52
 
38
53
  /** @type {VentoPluginOptions} */
39
54
  const options = {
@@ -52,10 +67,17 @@ export function VentoPlugin(eleventyConfig, userOptions) {
52
67
  ...userOptions,
53
68
  };
54
69
 
70
+ DEBUG.setup('Merged default and user config: %O', options);
71
+
55
72
  // Get list of filters, shortcodes and paired shortcodes
56
73
  const filters = eleventyConfig.getFilters();
74
+ DEBUG.setup('Reading filters from Eleventy: %o', filters);
75
+
57
76
  const shortcodes = eleventyConfig.getShortcodes();
77
+ DEBUG.setup('Reading shortcodes from Eleventy: %o', shortcodes);
78
+
58
79
  const pairedShortcodes = eleventyConfig.getPairedShortcodes();
80
+ DEBUG.setup('Reading paired shortcodes from Eleventy: %o', pairedShortcodes);
59
81
 
60
82
  // Add autotrim plugin if enabled
61
83
  if (options.autotrim) {
@@ -78,61 +100,79 @@ export function VentoPlugin(eleventyConfig, userOptions) {
78
100
  }
79
101
  }
80
102
 
103
+ DEBUG.setup('Enabled autotrim for tags: %o', tagSet);
104
+
81
105
  options.plugins.push(autotrimPlugin({ tags: [...tagSet] }));
82
106
  }
83
107
 
84
108
  // Add ignore tag plugin if enabled
85
109
  if (options.ignoreTag) {
110
+ DEBUG.setup('Enabling `{{! ... }}` tag syntax');
86
111
  options.plugins.push(ignoreTagPlugin);
87
112
  }
88
113
 
89
114
  // Create the vento engine instance
115
+ DEBUG.setup('Initializing Vento environment');
90
116
  const engine = createVentoEngine(options.ventoOptions);
91
117
 
92
118
  // Load plugins
119
+ DEBUG.setup('Loading plugins: %o', options.plugins);
93
120
  engine.loadPlugins(options.plugins);
94
121
 
95
122
  // Add filters, single and paired shortcodes if enabled
96
123
  if (options.filters) {
124
+ DEBUG.setup('Loading filters: %o', filters);
97
125
  engine.loadFilters(filters);
98
126
  }
99
127
  if (options.shortcodes) {
100
- engine.loadShortcodes(shortcodes, false);
128
+ DEBUG.setup('Loading shortcodes: %o', shortcodes);
129
+ engine.loadShortcodes(shortcodes);
101
130
  }
102
131
  if (options.pairedShortcodes) {
103
- engine.loadShortcodes(pairedShortcodes, true);
132
+ DEBUG.setup('Loading paired shortcodes: %o', pairedShortcodes);
133
+ engine.loadPairedShortcodes(pairedShortcodes);
104
134
  }
105
135
 
136
+ // Handle emptying the cache when files are updated
137
+ DEBUG.setup('Registering Vento cache handler on eleventy.beforeWatch event');
138
+ eleventyConfig.on('eleventy.beforeWatch', async (updatedFiles) => {
139
+ for (let file of updatedFiles) {
140
+ file = path.normalize(file);
141
+ DEBUG.cache('Delete cache entry for %s', file);
142
+ engine.cache.delete(file);
143
+ }
144
+ });
145
+
106
146
  // Add vto as a template format
147
+ DEBUG.setup('Registering .vto as a template format');
107
148
  eleventyConfig.addTemplateFormats('vto');
108
149
 
109
150
  // Add extension handling
151
+ DEBUG.setup('Registering .vto extension with eleventy');
110
152
  eleventyConfig.addExtension('vto', {
111
153
  outputFileExtension: 'html',
112
154
  read: true,
113
155
 
114
156
  // Main compile function
115
- async compile(inputContent, inputPath) {
116
- return async (data) => await engine.process({ data, source: inputContent, path: inputPath });
157
+ async compile(source, file) {
158
+ file = path.normalize(file);
159
+ return async (data) => await engine.process({ source, data, file });
117
160
  },
118
161
 
119
- // Custom permalink compilation
120
162
  compileOptions: {
121
- cache: true,
122
- getCacheKey(inputContent, inputPath) {
123
- if (engine.getCachedSource(inputPath) !== inputContent) {
124
- engine.emptyCache(inputPath);
125
- }
126
-
127
- return engine.getCachedSource(inputPath);
128
- },
129
- permalink(linkContents) {
130
- if (typeof linkContents !== 'string') {
131
- return linkContents;
163
+ // Defer all caching to Vento
164
+ cache: false,
165
+ // Custom permalink compilation
166
+ permalink(source, file) {
167
+ // 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 });
132
171
  }
133
172
 
134
- return async (data) => engine.process({ data, source: linkContents });
173
+ return source;
135
174
  },
136
175
  },
137
176
  });
177
+ DEBUG.setup('eleventy-plugin-vento initialized');
138
178
  }
@@ -1,19 +1,20 @@
1
1
  /**
2
2
  * @file Helper function that creates vento tags from eleventy functions
3
3
  *
4
- * @param {string} name
5
- * @param {boolean} paired
4
+ * @param {{name: string, group: 'shortcodes' | 'pairedShortcodes' }} options
6
5
  */
7
6
 
8
- export function createVentoTag(name, paired) {
7
+ export function createVentoTag(options) {
8
+ const IS_PAIRED = options.group === 'pairedShortcodes';
9
+
9
10
  /** @type {import("ventojs/src/environment.js").Tag} */
10
11
  const tag = (env, code, output, tokens) => {
11
- if (!code.startsWith(name)) return;
12
+ if (!code.startsWith(options.name)) return;
12
13
 
13
- // Declare helper object path strings
14
- const fn = `__env.utils._11ty.functions.${name}`;
14
+ // Declare helper variables for repeated strings in template
15
+ const fn = `__env.utils._11ty.${options.group}.${options.name}`;
15
16
  const ctx = '__env.utils._11ty.ctx';
16
- let args = [code.replace(name, '').trim()];
17
+ const args = [code.replace(options.name, '').trim()];
17
18
 
18
19
  const varname = output.startsWith('__shortcode_content')
19
20
  ? `${output}_precomp`
@@ -22,35 +23,38 @@ export function createVentoTag(name, paired) {
22
23
  // Create an array to hold compiled template code
23
24
  const compiled = [];
24
25
 
25
- if (paired) {
26
+ if (IS_PAIRED) {
26
27
  args.unshift(env.compileFilters(tokens, varname, env.options.autoescape));
27
28
  compiled.push(
28
29
  '{',
29
30
  `let ${varname} = "";`,
30
- ...env.compileTokens(tokens, varname, [`/${name}`])
31
+ ...env.compileTokens(tokens, varname, [`/${options.name}`])
31
32
  );
32
- if (tokens.length > 0 && (tokens[0][0] !== 'tag' || tokens[0][1] !== `/${name}`)) {
33
- throw new Error(`Missing closing tag for ${name} tag: ${code}`);
33
+ if (tokens.length > 0 && (tokens[0][0] !== 'tag' || tokens[0][1] !== `/${options.name}`)) {
34
+ throw new Error(`Vento: Missing closing tag for ${options.name} tag: ${code}`);
34
35
  }
35
36
  tokens.shift();
36
37
  }
37
38
 
38
- // Compile arguments into a string
39
- args = args.some(Boolean) ? `, ${args.filter(Boolean).join(', ')}` : '';
39
+ args.unshift(ctx);
40
40
 
41
41
  compiled.push(
42
42
  '{',
43
- `const __shortcode_result = await ${fn}.call(${ctx + args});`,
43
+ `const __shortcode_result = await ${fn}.call(${args.filter(Boolean).join(', ')});`,
44
44
  `${output} += ${env.compileFilters(tokens, '__shortcode_result', env.options.autoescape)}`,
45
45
  '}'
46
46
  );
47
47
 
48
- if (paired) compiled.push('}');
48
+ if (IS_PAIRED) {
49
+ compiled.push('}');
50
+ }
49
51
 
50
- // console.log(compiled);
52
+ const compiledTag = compiled.join('\n');
51
53
 
52
- return compiled.join('\n');
54
+ return compiledTag;
53
55
  };
54
56
 
55
- return Object.defineProperty(tag, 'name', { value: paired ? `${name}PairedTag` : `${name}Tag` });
57
+ return Object.defineProperty(tag, 'name', {
58
+ value: options.name + IS_PAIRED ? `PairedTag` : `Tag`,
59
+ });
56
60
  }
@@ -1,3 +1,12 @@
1
+ // Set up debugger global
2
+ import createDebugger from 'debug';
3
+ const debugBaseNamespace = 'Eleventy:Vento';
4
+ export const DEBUG = {
5
+ setup: createDebugger(`${debugBaseNamespace}:Setup`),
6
+ cache: createDebugger(`${debugBaseNamespace}:Cache`),
7
+ template: createDebugger(`${debugBaseNamespace}:Template`),
8
+ };
9
+
1
10
  // Project-wide constants
2
11
  export const REQUIRED_API_METHODS = [
3
12
  ['getShortcodes', 'v3.0.0-alpha.15'],
@@ -8,18 +17,4 @@ export const REQUIRED_API_METHODS = [
8
17
 
9
18
  export const CONTEXT_DATA_KEYS = ['page', 'eleventy'];
10
19
 
11
- // Project-wide helper functions
12
- export function logError(message) {
13
- console.error(`[eleventy-plugin-vento] ${message}`);
14
- }
15
-
16
- /** @param {import("@11ty/eleventy").UserConfig} configApi */
17
- export function runCompatibilityCheck(configApi) {
18
- for (const [method, version] of REQUIRED_API_METHODS) {
19
- if (!configApi?.[method]) {
20
- logError(
21
- `Eleventy plugin compatibility error: '${method}' not available. Use Eleventy ${version} or later.`
22
- );
23
- }
24
- }
25
- }
20
+ export const PERMALINK_PREFIX = 'EleventyVentoPermalink:';