eleventy-plugin-vento 3.2.0 → 4.0.0-beta.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,57 @@
1
1
  # eleventy-plugin-vento
2
2
 
3
+ ## 4.0.0-beta.0
4
+
5
+ ### Major Changes
6
+
7
+ - fc80b5f: Removed ignore tag `{{! ... }}` syntax. As mentioned in the 3.3.0 release notes, the now preferred way to "pass through" tags is with string literals.
8
+
9
+ You should update code with `{{! ... }}` to be `{{ '{{ }}' }}` like so:
10
+
11
+ ```diff
12
+ - {{! if condition }}
13
+ + {{ '{{ if condition }}' }}
14
+ do something
15
+ - {{! /if }}
16
+ - {{ '{{ /if }}' }}
17
+ ```
18
+
19
+ While more verbose, this change ensures that there isn't any ambiguity between a tag that needs ignoring and JS negation expressions.
20
+
21
+ ### Minor Changes
22
+
23
+ - 2b5f337: Rewrite project in Typescript and add a build/packaging step for releases and testing.
24
+
25
+ These changes also include `.d.ts` generation as part of the build step. If you're using `11ty.ts`'s `defineConfig` function, it will pick the types from this plugin up automatically. (resolves #22)
26
+
27
+ ## 3.3.0
28
+
29
+ ### Minor Changes
30
+
31
+ - 387eb48: ⚠️ DEPRECATION NOTICE: Ignore tag `{{! ... }}` syntax is now deprecated and will be removed in 4.0.0.
32
+
33
+ This change comes in favor of using string literals to "pass through" tags without rendering them, like so:
34
+
35
+ ```diff
36
+ - {{! if condition }}
37
+ + {{ '{{ if condition }}' }}
38
+ do something
39
+ - {{! /if }}
40
+ - {{ '{{ /if }}' }}
41
+ ```
42
+
43
+ While more verbose, this change ensures that there isn't any ambiguity between a tag that needs ignoring and JS negation expressions:
44
+
45
+ ```hbs
46
+ <!-- These are functionally equivalent and can lead to collisions in rendering functionality -->
47
+
48
+ {{! conditionStatementToIgnore }}
49
+
50
+ {{ !conditionStatementToIgnore }}
51
+ ```
52
+
53
+ If an improved syntax for deferred rendering scenarios ever makes its way to Vento, it will be made available in this plugin.
54
+
3
55
  ## 3.2.0
4
56
 
5
57
  ### Minor Changes
@@ -0,0 +1,46 @@
1
+ import { UserConfig } from '@11ty/eleventy';
2
+ import { Options } from 'ventojs';
3
+ import { Plugin } from 'ventojs/src/environment.js';
4
+
5
+ type AutotrimConfig = boolean | ('@11ty' | '@vento' | (string & Record<never, never>))[];
6
+ interface VentoPluginOptions {
7
+ /**
8
+ * An array of Vento plugins
9
+ */
10
+ plugins: Plugin[];
11
+ /**
12
+ * Enables Vento's autotrim plugin.
13
+ * ```js
14
+ * // Set to an array of tags
15
+ * autotrim: ['tag1', 'tag2']
16
+ *
17
+ * // Use Vento's defaults and Eleventy paired shortcodes
18
+ * // If set to `true`, uses both
19
+ * autotrim: ['@vento', '@11ty']
20
+ * autotrim: true
21
+ * ```
22
+ */
23
+ autotrim: AutotrimConfig;
24
+ /** Enable use of Eleventy filters */
25
+ filters: boolean;
26
+ /** Enable use of Eleventy shortcodes */
27
+ shortcodes: boolean;
28
+ /** Enable use of Eleventy paired shortcodes */
29
+ pairedShortcodes: boolean;
30
+ /**
31
+ * A Vento configuration object.
32
+ *
33
+ * **Note that `ventoOptions.includes` defaults to
34
+ * Eleventy's own `dir.includes` location.**
35
+ * @see {@link https://vento.js.org/configuration/}
36
+ */
37
+ ventoOptions: Options;
38
+ }
39
+
40
+ /**
41
+ * @file Main plugin declaration
42
+ */
43
+
44
+ declare function VentoPlugin(eleventyConfig: UserConfig, userOptions: Partial<VentoPluginOptions>): void;
45
+
46
+ export { VentoPlugin };
package/dist/index.js ADDED
@@ -0,0 +1,244 @@
1
+ // src/index.ts
2
+ import path from "node:path";
3
+ import autotrimPlugin, { defaultTags as autotrimDefaultTags } from "ventojs/plugins/auto_trim.js";
4
+
5
+ // src/engine.ts
6
+ import { default as ventojs } from "ventojs";
7
+
8
+ // src/modules/create-vento-tag.ts
9
+ function createVentoTag(spec) {
10
+ const IS_PAIRED = spec.group === "pairedShortcodes";
11
+ const tag = (env, code, output, tokens) => {
12
+ if (!code.startsWith(spec.name)) return;
13
+ const fn = `__env.utils._11tyFns.${spec.group}.${spec.name}`;
14
+ const ctx = "__env.utils._11tyCtx";
15
+ const args = [code.replace(spec.name, "").trim()];
16
+ const varname = output.startsWith("__shortcode_content") ? `${output}_precomp` : "__shortcode_content";
17
+ const compiled = [];
18
+ if (IS_PAIRED) {
19
+ args.unshift(env.compileFilters(tokens, varname, env.options.autoescape));
20
+ compiled.push(
21
+ "{",
22
+ `let ${varname} = "";`,
23
+ ...env.compileTokens(tokens, varname, [`/${spec.name}`])
24
+ );
25
+ if (tokens.length > 0 && (tokens[0][0] !== "tag" || tokens[0][1] !== `/${spec.name}`)) {
26
+ throw new Error(`Vento: Missing closing tag for ${spec.name} tag: ${code}`);
27
+ }
28
+ tokens.shift();
29
+ }
30
+ args.unshift(ctx);
31
+ compiled.push(
32
+ "{",
33
+ `const __shortcode_result = await ${fn}.call(${args.filter(Boolean).join(", ")});`,
34
+ `${output} += ${env.compileFilters(tokens, "__shortcode_result", env.options.autoescape)}`,
35
+ "}"
36
+ );
37
+ if (IS_PAIRED) {
38
+ compiled.push("}");
39
+ }
40
+ const compiledTag = compiled.join("\n");
41
+ return compiledTag;
42
+ };
43
+ return Object.defineProperty(tag, "name", {
44
+ value: spec.name + IS_PAIRED ? `PairedTag` : `Tag`
45
+ });
46
+ }
47
+
48
+ // src/modules/utils.ts
49
+ import createDebugger from "debug";
50
+ var debugBaseNamespace = "Eleventy:Vento";
51
+ var DEBUG = {
52
+ setup: createDebugger(`${debugBaseNamespace}:Setup`),
53
+ cache: createDebugger(`${debugBaseNamespace}:Cache`),
54
+ render: createDebugger(`${debugBaseNamespace}:Render`)
55
+ };
56
+ var REQUIRED_API_METHODS = [
57
+ ["getShortcodes", "v3.0.0-alpha.15"],
58
+ ["getPairedShortcodes", "v3.0.0-alpha.15"],
59
+ ["getFilters", "v3.0.0-alpha.15"],
60
+ ["addExtension", "v1.0.0"]
61
+ ];
62
+ var CONTEXT_DATA_KEYS = ["page", "eleventy"];
63
+ function runCompatibilityCheck(config) {
64
+ DEBUG.setup("Run compatibility check");
65
+ for (const [method, version] of REQUIRED_API_METHODS) {
66
+ if (!(method in config)) {
67
+ console.error(
68
+ "[eleventy-plugin-vento] Plugin compatibility error:",
69
+ `\`${method}\``,
70
+ "not found. Please use Eleventy",
71
+ version,
72
+ "or later."
73
+ );
74
+ }
75
+ }
76
+ }
77
+
78
+ // src/engine.ts
79
+ function createVentoEngine(options) {
80
+ const env = ventojs(options);
81
+ env.utils._11tyFns = { shortcodes: {}, pairedShortcodes: {} };
82
+ env.utils._11tyCtx = {};
83
+ function setContext(newContext) {
84
+ if (env.utils._11tyCtx?.page?.inputPath === newContext?.page?.inputPath) {
85
+ return;
86
+ }
87
+ for (const K of CONTEXT_DATA_KEYS) {
88
+ env.utils._11tyCtx[K] = newContext[K];
89
+ }
90
+ DEBUG.setup("Reload context, new context is: %o", env.utils._11tyCtx);
91
+ }
92
+ function loadPlugins(plugins) {
93
+ for (const plugin of plugins) {
94
+ env.use(plugin);
95
+ }
96
+ }
97
+ function loadFilters(filters) {
98
+ for (const [name, fn] of Object.entries(filters)) {
99
+ env.filters[name] = fn.bind(env.utils._11tyCtx);
100
+ }
101
+ }
102
+ function loadShortcodes(shortcodes) {
103
+ for (const [name, fn] of Object.entries(shortcodes)) {
104
+ env.utils._11tyFns.shortcodes[name] = fn;
105
+ env.tags.push(createVentoTag({ name, group: "shortcodes" }));
106
+ }
107
+ }
108
+ function loadPairedShortcodes(pairedShortcodes) {
109
+ for (const [name, fn] of Object.entries(pairedShortcodes)) {
110
+ env.utils._11tyFns.pairedShortcodes[name] = fn;
111
+ env.tags.push(createVentoTag({ name, group: "pairedShortcodes" }));
112
+ }
113
+ }
114
+ function getTemplateFunction(source, file, useVentoCache = true) {
115
+ let template = env.cache.get(file);
116
+ if (template?.source === source) {
117
+ DEBUG.cache("Cache HIT for `%s`, used precompiled template", file);
118
+ } else {
119
+ template = env.compile(source, file);
120
+ DEBUG.cache(
121
+ `Cache MISS for \`%s\`, compiled new template:
122
+ Use Vento cache: %o
123
+
124
+ ${template}`,
125
+ file,
126
+ useVentoCache
127
+ );
128
+ if (useVentoCache) {
129
+ env.cache.set(file, template);
130
+ }
131
+ }
132
+ return template;
133
+ }
134
+ async function render(template, data, from) {
135
+ setContext(data);
136
+ DEBUG.render("Rendering `%s`", from);
137
+ const { content } = await template(data);
138
+ return content;
139
+ }
140
+ return {
141
+ cache: env.cache,
142
+ loadPlugins,
143
+ loadFilters,
144
+ loadShortcodes,
145
+ loadPairedShortcodes,
146
+ getTemplateFunction,
147
+ render
148
+ };
149
+ }
150
+
151
+ // src/index.ts
152
+ function VentoPlugin(eleventyConfig, userOptions) {
153
+ DEBUG.setup("Initializing eleventy-plugin-vento");
154
+ runCompatibilityCheck(eleventyConfig);
155
+ const options = {
156
+ // Define defaults
157
+ autotrim: false,
158
+ plugins: [],
159
+ filters: true,
160
+ shortcodes: true,
161
+ pairedShortcodes: true,
162
+ ventoOptions: {
163
+ includes: eleventyConfig.directories.includes
164
+ },
165
+ // Merge in user-provided options
166
+ ...userOptions
167
+ };
168
+ DEBUG.setup("Merged default and user config: %O", options);
169
+ const filters = eleventyConfig.getFilters();
170
+ DEBUG.setup("Reading filters from Eleventy: %o", filters);
171
+ const shortcodes = eleventyConfig.getShortcodes();
172
+ DEBUG.setup("Reading shortcodes from Eleventy: %o", shortcodes);
173
+ const pairedShortcodes = eleventyConfig.getPairedShortcodes();
174
+ DEBUG.setup("Reading paired shortcodes from Eleventy: %o", pairedShortcodes);
175
+ if (options.autotrim) {
176
+ const defaults = ["@vento", "@11ty"];
177
+ const tagSet = new Set(options.autotrim === true ? defaults : options.autotrim);
178
+ if (tagSet.has("@vento")) {
179
+ tagSet.delete("@vento");
180
+ for (const name of autotrimDefaultTags) {
181
+ tagSet.add(name);
182
+ }
183
+ }
184
+ if (tagSet.has("@11ty")) {
185
+ tagSet.delete("@11ty");
186
+ for (const name in pairedShortcodes) {
187
+ tagSet.add(name);
188
+ }
189
+ }
190
+ DEBUG.setup("Enabled autotrim for tags: %o", tagSet);
191
+ options.plugins.push(autotrimPlugin({ tags: [...tagSet] }));
192
+ }
193
+ DEBUG.setup("Initializing Vento environment");
194
+ const engine = createVentoEngine(options.ventoOptions);
195
+ DEBUG.setup("Loading plugins: %o", options.plugins);
196
+ engine.loadPlugins(options.plugins);
197
+ if (options.filters) {
198
+ DEBUG.setup("Loading filters: %o", filters);
199
+ engine.loadFilters(filters);
200
+ }
201
+ if (options.shortcodes) {
202
+ DEBUG.setup("Loading shortcodes: %o", shortcodes);
203
+ engine.loadShortcodes(shortcodes);
204
+ }
205
+ if (options.pairedShortcodes) {
206
+ DEBUG.setup("Loading paired shortcodes: %o", pairedShortcodes);
207
+ engine.loadPairedShortcodes(pairedShortcodes);
208
+ }
209
+ DEBUG.setup("Registering Vento cache handler on eleventy.beforeWatch event");
210
+ eleventyConfig.on("eleventy.beforeWatch", async (updatedFiles) => {
211
+ for (let file of updatedFiles) {
212
+ file = path.normalize(file);
213
+ DEBUG.cache("Delete cache entry for %s", file);
214
+ engine.cache.delete(file);
215
+ }
216
+ });
217
+ DEBUG.setup("Registering .vto as a template format");
218
+ eleventyConfig.addTemplateFormats("vto");
219
+ DEBUG.setup("Registering .vto extension with eleventy");
220
+ eleventyConfig.addExtension("vto", {
221
+ outputFileExtension: "html",
222
+ read: true,
223
+ compile(inputContent, inputPath) {
224
+ inputPath = path.normalize(inputPath);
225
+ const template = engine.getTemplateFunction(inputContent, inputPath, false);
226
+ return async (data) => await engine.render(template, data, inputPath);
227
+ },
228
+ compileOptions: {
229
+ // Custom permalink compilation
230
+ permalink(permalinkContent, inputPath) {
231
+ if (typeof permalinkContent === "string" && /\{\{\s+.+\s+\}\}/.test(permalinkContent)) {
232
+ inputPath = "Permalink::" + path.normalize(inputPath);
233
+ const template = engine.getTemplateFunction(permalinkContent, inputPath);
234
+ return async (data) => await engine.render(template, data, inputPath);
235
+ }
236
+ return permalinkContent;
237
+ }
238
+ }
239
+ });
240
+ DEBUG.setup("eleventy-plugin-vento initialized");
241
+ }
242
+ export {
243
+ VentoPlugin
244
+ };
package/package.json CHANGED
@@ -1,16 +1,23 @@
1
1
  {
2
2
  "name": "eleventy-plugin-vento",
3
- "version": "3.2.0",
3
+ "version": "4.0.0-beta.0",
4
4
  "description": "Adds support for the Vento templating language to Eleventy.",
5
5
  "type": "module",
6
6
  "engines": {
7
- "node": ">=16"
7
+ "node": ">=18"
8
8
  },
9
9
  "exports": {
10
10
  ".": {
11
- "import": "./src/index.js"
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
12
13
  }
13
14
  },
15
+ "files": [
16
+ "dist/*",
17
+ "LICENSE",
18
+ "CHANGELOG.md",
19
+ "readme.md"
20
+ ],
14
21
  "repository": "git@github.com:noelforte/eleventy-plugin-vento.git",
15
22
  "bugs": {
16
23
  "url": "https://github.com/noelforte/eleventy-plugin-vento/issues"
@@ -27,23 +34,25 @@
27
34
  "url": "https://forte.is/"
28
35
  },
29
36
  "license": "MIT",
30
- "files": [
31
- "src/*",
32
- "CHANGELOG.md"
33
- ],
34
37
  "dependencies": {
35
38
  "debug": "4.3.7",
36
39
  "ventojs": "1.12.10"
37
40
  },
38
41
  "devDependencies": {
42
+ "11ty.ts": "0.0.5",
39
43
  "@11ty/eleventy": "3.0.0",
40
44
  "@changesets/cli": "2.27.9",
41
45
  "@eslint/js": "9.13.0",
46
+ "@types/debug": "4.1.12",
47
+ "@types/eslint-config-prettier": "6.11.3",
48
+ "@types/eslint__js": "8.42.3",
49
+ "@types/node": "22.8.4",
42
50
  "eslint": "9.13.0",
43
- "eslint-config-flat-gitignore": "0.3.0",
44
51
  "eslint-config-prettier": "9.1.0",
45
52
  "eslint-plugin-unicorn": "56.0.0",
46
- "globals": "15.11.0",
53
+ "tsup": "8.3.5",
54
+ "typescript": "5.6.3",
55
+ "typescript-eslint": "8.12.2",
47
56
  "vitest": "2.1.4"
48
57
  },
49
58
  "prettier": {
@@ -53,8 +62,11 @@
53
62
  "trailingComma": "es5"
54
63
  },
55
64
  "scripts": {
56
- "test": "vitest",
57
- "lint": "eslint '**/*.js'",
65
+ "test": "vitest --run",
66
+ "check": "pnpm --color run '/ts-check|lint/'",
67
+ "lint": "eslint '.'",
68
+ "ts-check": "tsc --noEmit",
69
+ "build": "tsup",
58
70
  "ci:version": "changeset version && pnpm install --no-frozen-lockfile",
59
71
  "ci:publish": "changeset publish",
60
72
  "ci:generate-changesets": "node ./scripts/generate-changesets.js",
package/readme.md CHANGED
@@ -16,7 +16,6 @@ An [Eleventy](https://11ty.dev/) plugin that adds support for [Vento](https://ve
16
16
  [Shortcodes (Single & Paired)](#shortcodes-single--paired)<br>
17
17
  [Vento Plugins](#vento-plugins)<br>
18
18
  [Auto-Trimming Tags](#auto-trimming-tags)<br>
19
- [Ignoring Tags](#ignoring-tags)<br>
20
19
  [Debugging](#debugging)<br>
21
20
 
22
21
  ## Installing
@@ -136,7 +135,7 @@ For paired shortcodes, the syntax is the same, just add a closing tag. Paired sh
136
135
 
137
136
  If you'd prefer to set shortcodes yourself (via a plugin or other method) or prevent Eleventy from loading shortcodes into Vento, set `shortcodes: false` and/or `pairedShortcodes: false` in the plugin options.
138
137
 
139
- > [!CAUTION]
138
+ > [!IMPORTANT]
140
139
  > While it's straightforward to load filters via a Vento plugin that appends filters to the filters object as `env.filters.[filter_name]()`, creating custom tags in Vento is more involved. It's highly advised to keep these two options enabled unless you know what you're doing.
141
140
 
142
141
  ### Relevant Documentation
@@ -199,74 +198,6 @@ eleventyConfig.addPlugin(VentoPlugin, {
199
198
 
200
199
  Vento: See [Auto Trim Plugin](https://vento.js.org/plugins/auto-trim/).
201
200
 
202
- ## Ignoring Tags
203
-
204
- 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.
205
-
206
- This feature is enabled through use of the `enableIgnoreTag` plugin option.
207
-
208
- ```js
209
- eleventyConfig.addPlugin(VentoPlugin, {
210
- enableIgnoreTag: true,
211
- });
212
- ```
213
-
214
- To skip over a tag, add a `!` directly after the opening tag.
215
-
216
- ```nunjucks
217
- <!-- This: -->
218
- {{! doSomeServerSideStuff() }}
219
-
220
- <!-- Renders as: -->
221
- {{ doSomeServerSideStuff() }}
222
- ```
223
-
224
- Works with JS expressions too:
225
-
226
- ```nunjucks
227
- {{!> 2 + 2 }}
228
-
229
- <!-- Renders as: -->
230
- {{> 2 + 2 }}
231
- ```
232
-
233
- 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.
234
-
235
- Consider the following:
236
-
237
- ```nunjucks
238
- {{ echo }}
239
- <!-- Will be preserved in output -->
240
- {{ if someCondition }}
241
- <p>{{ getServerData }}</p>
242
- {{ /echo }}
243
-
244
- <!-- Can't render this without exiting and re-entering echo -->
245
- <p>Page built on: {{ localBuildTime }}</p>
246
-
247
- {{ echo }}
248
- {{ /if }}
249
- {{ /echo }}
250
- ```
251
-
252
- Rather than having to do multiple `{{ /echo }} ... {{ echo }}` statements, the previous can be rewritten as such:
253
-
254
- ```nunjucks
255
- <!-- This: -->
256
- {{! if someCondition }}
257
- <p>{{! getServerData }}</p>
258
-
259
- <p>Page built on: {{ localBuildTime }}</p>
260
- {{! /if }}
261
-
262
- <!-- Renders as: -->
263
- {{ if someCondition }}
264
- <p>{{ getServerData }}</p>
265
-
266
- <p>Page built on: 09-19-2024</p>
267
- {{ /if }}
268
- ```
269
-
270
201
  ## Debugging
271
202
 
272
203
  Like Eleventy, this plugin uses the [`debug`](https://www.npmjs.com/package/debug) package to handle verbose logging. The following `DEBUG` namespaces are implemented:
package/src/engine.js DELETED
@@ -1,126 +0,0 @@
1
- /**
2
- * @file Function that handles creating the Vento environment. Exposes
3
- * a simple API for Eleventy to interface with.
4
- *
5
- * @typedef {{eleventy?: Record<string, unknown>, page?: Record<string, unknown>}} EleventyContext
6
- * @typedef {EleventyContext & Record<string, unknown>} EleventyData
7
- * @typedef {(...args: unknown[]) => unknown} EleventyFunction
8
- * @typedef {Record<string, EleventyFunction>} EleventyFunctionSet
9
- * @typedef {import('ventojs/src/environment.js').Environment} VentoEnvironment
10
- * @typedef {VentoEnvironment & {
11
- * utils: {
12
- * _11tyFns: { shortcodes: EleventyFunctionSet, pairedShortcodes: EleventyFunctionSet }
13
- * _11tyCtx: EleventyContext
14
- * }
15
- * }} EleventyVentoEnvironment
16
- */
17
-
18
- // External library
19
- import ventojs from 'ventojs';
20
-
21
- // Internal modules
22
- import { createVentoTag } from './modules/create-vento-tag.js';
23
- import { CONTEXT_DATA_KEYS, DEBUG } from './modules/utils.js';
24
-
25
- /** @param {import('ventojs').Options} options */
26
- export function createVentoEngine(options) {
27
- /** @type {EleventyVentoEnvironment} */
28
- const env = ventojs(options);
29
- env.utils._11tyFns = { shortcodes: {}, pairedShortcodes: {} };
30
- env.utils._11tyCtx = {};
31
-
32
- /** @param {EleventyData} newContext */
33
- function setContext(newContext) {
34
- if (env.utils._11tyCtx?.page?.inputPath === newContext?.page?.inputPath) {
35
- return;
36
- }
37
-
38
- for (const K of CONTEXT_DATA_KEYS) {
39
- env.utils._11tyCtx[K] = newContext[K];
40
- }
41
-
42
- DEBUG.setup('Reload context, new context is: %o', env.utils._11tyCtx);
43
- }
44
-
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
-
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
- }
58
-
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);
96
- }
97
- }
98
-
99
- return template;
100
- }
101
-
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);
110
-
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,
125
- };
126
- }
package/src/index.js DELETED
@@ -1,177 +0,0 @@
1
- /**
2
- * @file Main plugin declaration
3
- *
4
- * @typedef VentoPluginOptions
5
- * @prop {import('ventojs/src/environment.js').Plugin[]} plugins
6
- * Array of vento plugins to use when compiling templates
7
- * @prop {boolean|string[]} autotrim
8
- * Enable Vento's [`autoTrim`](https://vento.js.org/plugins/auto-trim/)
9
- * plugin to remove whitespace from tags in output
10
- * @prop {boolean} [shortcodes=true]
11
- * Create vento tags for Eleventy [Shortcodes](https://www.11ty.dev/docs/shortcodes/)
12
- * @prop {boolean} [pairedShortcodes=true]
13
- * Create vento tags for Eleventy [Paired Shortcodes](https://www.11ty.dev/docs/shortcodes/#paired-shortcodes)
14
- * @prop {boolean} [filters=true]
15
- * Create vento filters for Eleventy [Filters](https://www.11ty.dev/docs/filters/)
16
- * @prop {boolean} [ignoreTag=false]
17
- * Enables/disables tag ignore (`{{! ... }}`) syntax in templates
18
- * @prop {import('ventojs').Options} ventoOptions
19
- * Options to pass on to the `ventojs` engine.
20
- * (See [Vento Docs](https://vento.js.org/configuration/#options))
21
- */
22
-
23
- // Built-ins
24
- import path from 'node:path';
25
-
26
- // External modules
27
- import autotrimPlugin, { defaultTags as autotrimDefaultTags } from 'ventojs/plugins/auto_trim.js';
28
-
29
- // Local modules
30
- import { createVentoEngine } from './engine.js';
31
- import { ignoreTagPlugin } from './modules/ignore-tag.js';
32
- import { DEBUG, runCompatibilityCheck } from './modules/utils.js';
33
-
34
- /**
35
- * @param {import('@11ty/eleventy').UserConfig} eleventyConfig
36
- * @param {Partial<VentoPluginOptions>} userOptions
37
- */
38
- export function VentoPlugin(eleventyConfig, userOptions) {
39
- DEBUG.setup('Initializing eleventy-plugin-vento');
40
- runCompatibilityCheck(eleventyConfig);
41
-
42
- /** @type {VentoPluginOptions} */
43
- const options = {
44
- // Define defaults
45
- autotrim: false,
46
- plugins: [],
47
- filters: true,
48
- shortcodes: true,
49
- pairedShortcodes: true,
50
- ignoreTag: false,
51
- ventoOptions: {
52
- includes: eleventyConfig.directories.includes,
53
- },
54
-
55
- // Merge in user-provided options
56
- ...userOptions,
57
- };
58
-
59
- DEBUG.setup('Merged default and user config: %O', options);
60
-
61
- // Get list of filters, shortcodes and paired shortcodes
62
- const filters = eleventyConfig.getFilters();
63
- DEBUG.setup('Reading filters from Eleventy: %o', filters);
64
-
65
- const shortcodes = eleventyConfig.getShortcodes();
66
- DEBUG.setup('Reading shortcodes from Eleventy: %o', shortcodes);
67
-
68
- const pairedShortcodes = eleventyConfig.getPairedShortcodes();
69
- DEBUG.setup('Reading paired shortcodes from Eleventy: %o', pairedShortcodes);
70
-
71
- // Add autotrim plugin if enabled
72
- if (options.autotrim) {
73
- const defaults = ['@vento', '@11ty'];
74
-
75
- /** @type {Set<string>} */
76
- const tagSet = new Set(options.autotrim === true ? defaults : options.autotrim);
77
-
78
- if (tagSet.has('@vento')) {
79
- tagSet.delete('@vento');
80
- for (const name of autotrimDefaultTags) {
81
- tagSet.add(name);
82
- }
83
- }
84
-
85
- if (tagSet.has('@11ty')) {
86
- tagSet.delete('@11ty');
87
- for (const name in pairedShortcodes) {
88
- tagSet.add(name);
89
- }
90
- }
91
-
92
- DEBUG.setup('Enabled autotrim for tags: %o', tagSet);
93
-
94
- options.plugins.push(autotrimPlugin({ tags: [...tagSet] }));
95
- }
96
-
97
- // Add ignore tag plugin if enabled
98
- if (options.ignoreTag) {
99
- DEBUG.setup('Enabling `{{! ... }}` tag syntax');
100
- options.plugins.push(ignoreTagPlugin);
101
- }
102
-
103
- // Create the vento engine instance
104
- DEBUG.setup('Initializing Vento environment');
105
- const engine = createVentoEngine(options.ventoOptions);
106
-
107
- // Load plugins
108
- DEBUG.setup('Loading plugins: %o', options.plugins);
109
- engine.loadPlugins(options.plugins);
110
-
111
- // Add filters, single and paired shortcodes if enabled
112
- if (options.filters) {
113
- DEBUG.setup('Loading filters: %o', filters);
114
- engine.loadFilters(filters);
115
- }
116
- if (options.shortcodes) {
117
- DEBUG.setup('Loading shortcodes: %o', shortcodes);
118
- engine.loadShortcodes(shortcodes);
119
- }
120
- if (options.pairedShortcodes) {
121
- DEBUG.setup('Loading paired shortcodes: %o', pairedShortcodes);
122
- engine.loadPairedShortcodes(pairedShortcodes);
123
- }
124
-
125
- // Handle emptying the cache when files are updated
126
- DEBUG.setup('Registering Vento cache handler on eleventy.beforeWatch event');
127
- eleventyConfig.on('eleventy.beforeWatch', async (updatedFiles) => {
128
- for (let file of updatedFiles) {
129
- file = path.normalize(file);
130
- DEBUG.cache('Delete cache entry for %s', file);
131
- engine.cache.delete(file);
132
- }
133
- });
134
-
135
- // Add vto as a template format
136
- DEBUG.setup('Registering .vto as a template format');
137
- eleventyConfig.addTemplateFormats('vto');
138
-
139
- // Add extension handling
140
- DEBUG.setup('Registering .vto extension with eleventy');
141
- eleventyConfig.addExtension('vto', {
142
- outputFileExtension: 'html',
143
- read: true,
144
-
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);
154
- },
155
-
156
- compileOptions: {
157
- // Custom permalink compilation
158
- permalink(permalinkContent, inputPath) {
159
- // Short circuit if input isn't a string and doesn't look like a vento template
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);
169
- }
170
-
171
- return permalinkContent;
172
- },
173
- },
174
- });
175
-
176
- DEBUG.setup('eleventy-plugin-vento initialized');
177
- }
@@ -1,60 +0,0 @@
1
- /**
2
- * @file Helper function that creates vento tags from eleventy functions
3
- *
4
- * @param {{name: string, group: 'shortcodes' | 'pairedShortcodes' }} options
5
- */
6
-
7
- export function createVentoTag(options) {
8
- const IS_PAIRED = options.group === 'pairedShortcodes';
9
-
10
- /** @type {import("ventojs/src/environment.js").Tag} */
11
- const tag = (env, code, output, tokens) => {
12
- if (!code.startsWith(options.name)) return;
13
-
14
- // Declare helper variables for repeated strings in template
15
- const fn = `__env.utils._11tyFns.${options.group}.${options.name}`;
16
- const ctx = '__env.utils._11tyCtx';
17
- const args = [code.replace(options.name, '').trim()];
18
-
19
- const varname = output.startsWith('__shortcode_content')
20
- ? `${output}_precomp`
21
- : '__shortcode_content';
22
-
23
- // Create an array to hold compiled template code
24
- const compiled = [];
25
-
26
- if (IS_PAIRED) {
27
- args.unshift(env.compileFilters(tokens, varname, env.options.autoescape));
28
- compiled.push(
29
- '{',
30
- `let ${varname} = "";`,
31
- ...env.compileTokens(tokens, varname, [`/${options.name}`])
32
- );
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}`);
35
- }
36
- tokens.shift();
37
- }
38
-
39
- args.unshift(ctx);
40
-
41
- compiled.push(
42
- '{',
43
- `const __shortcode_result = await ${fn}.call(${args.filter(Boolean).join(', ')});`,
44
- `${output} += ${env.compileFilters(tokens, '__shortcode_result', env.options.autoescape)}`,
45
- '}'
46
- );
47
-
48
- if (IS_PAIRED) {
49
- compiled.push('}');
50
- }
51
-
52
- const compiledTag = compiled.join('\n');
53
-
54
- return compiledTag;
55
- };
56
-
57
- return Object.defineProperty(tag, 'name', {
58
- value: options.name + IS_PAIRED ? `PairedTag` : `Tag`,
59
- });
60
- }
@@ -1,15 +0,0 @@
1
- /**
2
- * @file Definition for tag that preserves vento syntax in output
3
- */
4
-
5
- /** @type {import('ventojs/src/environment.js').Tag} */
6
- function ignoreTag(_env, code, output, _tokens) {
7
- if (!code.startsWith('!')) return;
8
- const compiled = `{{${code.replace(/!(>?)\s+/, '$1 ')}}}`;
9
- return `${output} += "${compiled}";`;
10
- }
11
-
12
- /** @type {import('ventojs/src/environment.js').Plugin} */
13
- export function ignoreTagPlugin(env) {
14
- env.tags.push(ignoreTag);
15
- }
@@ -1,34 +0,0 @@
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
- render: createDebugger(`${debugBaseNamespace}:Render`),
8
- };
9
-
10
- // Project-wide constants
11
- export const REQUIRED_API_METHODS = [
12
- ['getShortcodes', 'v3.0.0-alpha.15'],
13
- ['getPairedShortcodes', 'v3.0.0-alpha.15'],
14
- ['getFilters', 'v3.0.0-alpha.15'],
15
- ['addExtension', 'v1.0.0'],
16
- ];
17
-
18
- export const CONTEXT_DATA_KEYS = ['page', 'eleventy'];
19
-
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
- }