ventojs 0.6.0 → 0.7.1

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/README.md CHANGED
@@ -91,13 +91,26 @@ First, let's take a look at this syntax example:
91
91
 
92
92
  ## Getting started
93
93
 
94
- This is a library for Deno. I'm planning to release an NPM version in the
95
- future.
94
+ This is a library for Deno. ~~I'm planning to release an NPM version in the
95
+ future.~~
96
+ [There's already an NPM version](https://www.npmjs.com/package/ventojs) that you
97
+ can install with `npm install ventojs`.
96
98
 
97
- First, you need to import the library and create an instance:
99
+ Import the library and create an instance:
98
100
 
99
101
  ```ts
100
- import vento from "https://deno.land/x/vento@v0.2.0/mod.ts";
102
+ import vento from "https://deno.land/x/vento/mod.ts";
103
+
104
+ const vto = vento({
105
+ // Resolve the non-relative includes paths
106
+ includes: "./path/to/includes",
107
+ });
108
+ ```
109
+
110
+ Or in Node:
111
+
112
+ ```ts
113
+ import vento from "ventojs";
101
114
 
102
115
  const vto = vento({
103
116
  // Resolve the non-relative includes paths
@@ -113,19 +126,22 @@ can use `load` to load and compile a template file and return it.
113
126
  const template = vto.load("my-template.vto");
114
127
 
115
128
  // Now you can use it passing the data
116
- template({ title: "Hello world" });
129
+ const result = template({ title: "Hello world" });
130
+ console.log(result.content);
117
131
  ```
118
132
 
119
133
  Alternatively, you can load and run the template file in a single call:
120
134
 
121
135
  ```ts
122
- vto.run("my-template.vto", { title: "Hello world" });
136
+ const result = vto.run("my-template.vto", { title: "Hello world" });
137
+ console.log(result.content);
123
138
  ```
124
139
 
125
140
  If the template code is not a file, you can run it directly:
126
141
 
127
142
  ```ts
128
- vto.runString("<h1>{{ title }}</h1>", { title: "Hello world" });
143
+ const result = vto.runString("<h1>{{ title }}</h1>", { title: "Hello world" });
144
+ console.log(result.content);
129
145
  ```
130
146
 
131
147
  ## Visual Studio Code Support
package/esm/mod.js CHANGED
@@ -7,6 +7,9 @@ import includeTag from "./plugins/include.js";
7
7
  import setTag from "./plugins/set.js";
8
8
  import jsTag from "./plugins/js.js";
9
9
  import layoutTag from "./plugins/layout.js";
10
+ import functionTag from "./plugins/function.js";
11
+ import importTag from "./plugins/import.js";
12
+ import exportTag from "./plugins/export.js";
10
13
  import escape from "./plugins/escape.js";
11
14
  export default function (options = {}) {
12
15
  const loader = typeof options.includes === "object"
@@ -23,6 +26,9 @@ export default function (options = {}) {
23
26
  env.use(includeTag());
24
27
  env.use(setTag());
25
28
  env.use(layoutTag());
29
+ env.use(functionTag());
30
+ env.use(importTag());
31
+ env.use(exportTag());
26
32
  env.use(escape());
27
33
  return env;
28
34
  }
@@ -0,0 +1,2 @@
1
+ import type { Environment } from "../src/environment.js";
2
+ export default function (): (env: Environment) => void;
@@ -0,0 +1,46 @@
1
+ export default function () {
2
+ return (env) => {
3
+ env.tags.push(exportTag);
4
+ };
5
+ }
6
+ function exportTag(env, code, _output, tokens) {
7
+ if (!code.startsWith("export ")) {
8
+ return;
9
+ }
10
+ const expression = code.replace(/^export\s+/, "");
11
+ // Value is set (e.g. {{ export foo = "bar" }})
12
+ if (expression.includes("=")) {
13
+ const match = code.match(/^export\s+([\w]+)\s*=\s*([\s\S]+)$/);
14
+ if (!match) {
15
+ throw new Error(`Invalid export tag: ${code}`);
16
+ }
17
+ const [, variable, value] = match;
18
+ const val = env.compileFilters(tokens, value);
19
+ return `if (__data.hasOwnProperty("${variable}")) {
20
+ ${variable} = ${val};
21
+ } else {
22
+ var ${variable} = ${val};
23
+ }
24
+ __data["${variable}"] = ${variable};
25
+ __exports["${variable}"] = ${variable};
26
+ `;
27
+ }
28
+ // Value is captured (eg: {{ export foo }}bar{{ /export }})
29
+ const compiled = [];
30
+ const compiledFilters = env.compileFilters(tokens, expression);
31
+ compiled.push(`if (__data.hasOwnProperty("${expression}")) {
32
+ ${expression} = "";
33
+ } else {
34
+ var ${expression} = "";
35
+ }
36
+ `);
37
+ compiled.push(...env.compileTokens(tokens, expression, ["/export"]));
38
+ if (tokens.length && (tokens[0][0] !== "tag" || tokens[0][1] !== "/export")) {
39
+ throw new Error(`Missing closing tag for export tag: ${code}`);
40
+ }
41
+ tokens.shift();
42
+ compiled.push(`${expression} = ${compiledFilters};`);
43
+ compiled.push(`__data["${expression.trim()}"] = ${expression};`);
44
+ compiled.push(`__exports["${expression.trim()}"] = ${expression};`);
45
+ return compiled.join("\n");
46
+ }
@@ -0,0 +1,2 @@
1
+ import type { Environment } from "../src/environment.js";
2
+ export default function (): (env: Environment) => void;
@@ -0,0 +1,37 @@
1
+ export default function () {
2
+ return (env) => {
3
+ env.tags.push(functionTag);
4
+ };
5
+ }
6
+ function functionTag(env, code, _output, tokens) {
7
+ if (!code.match(/(export\s+)?(async\s+)?function\s/)) {
8
+ return;
9
+ }
10
+ const match = code.match(/^(export\s+)?(async\s+)?function\s+(\w+)\s*(\([^)]+\))?$/);
11
+ if (!match) {
12
+ throw new Error(`Invalid function: ${code}`);
13
+ }
14
+ const [_, exp, as, name, args] = match;
15
+ const compiled = [];
16
+ compiled.push(`${as || ""} function ${name} ${args || "()"} {`);
17
+ compiled.push(`let __output = "";`);
18
+ if (exp) {
19
+ compiled.push(...env.compileTokens(tokens, "__output", ["/export"]));
20
+ if (tokens.length && (tokens[0][0] !== "tag" || tokens[0][1] !== "/export")) {
21
+ throw new Error(`Missing closing tag for export function tag: ${code}`);
22
+ }
23
+ }
24
+ else {
25
+ compiled.push(...env.compileTokens(tokens, "__output", ["/function"]));
26
+ if (tokens.length && (tokens[0][0] !== "tag" || tokens[0][1] !== "/function")) {
27
+ throw new Error(`Missing closing tag for function tag: ${code}`);
28
+ }
29
+ }
30
+ tokens.shift();
31
+ compiled.push(`return __output;`);
32
+ compiled.push(`}`);
33
+ if (exp) {
34
+ compiled.push(`__exports["${name}"] = ${name}`);
35
+ }
36
+ return compiled.join("\n");
37
+ }
@@ -0,0 +1,2 @@
1
+ import type { Environment } from "../src/environment.js";
2
+ export default function (): (env: Environment) => void;
@@ -0,0 +1,24 @@
1
+ export default function () {
2
+ return (env) => {
3
+ env.tags.push(importTag);
4
+ };
5
+ }
6
+ function importTag(_env, code) {
7
+ if (!code.startsWith("import ")) {
8
+ return;
9
+ }
10
+ const match = code?.match(/^import\s+(\{[\s|\S]*\}|\w+)\s+from\s+(.+)$/);
11
+ if (!match) {
12
+ throw new Error(`Invalid import: ${code}`);
13
+ }
14
+ const [_, vars, file] = match;
15
+ const compiled = [];
16
+ compiled.push(`__tmp = await __env.run(${file}, {...__data}, __file);`);
17
+ if (vars.startsWith("{")) {
18
+ compiled.push(`let ${vars} = __tmp;`);
19
+ }
20
+ else {
21
+ compiled.push(`let ${vars} = __tmp;`);
22
+ }
23
+ return compiled.join("\n");
24
+ }
@@ -12,5 +12,11 @@ function includeTag(_env, code, output) {
12
12
  throw new Error(`Invalid include: ${code}`);
13
13
  }
14
14
  const [_, file, data] = match;
15
- return `${output} += await __env.run(${file}, {...__data${data ? `, ${data}` : ""}}, __file);`;
15
+ return `{
16
+ __tmp = await __env.run(${file},
17
+ {...__data${data ? `, ${data}` : ""}},
18
+ __file
19
+ );
20
+ ${output} += __tmp.content;
21
+ }`;
16
22
  }
@@ -23,7 +23,11 @@ function layoutTag(env, code, output, tokens) {
23
23
  }
24
24
  tokens.shift();
25
25
  compiled.push(`${varname} = ${compiledFilters};`);
26
- compiled.push(`${output} += await __env.run(${file}, {...__data${data ? `, ${data}` : ""}, content: ${varname}}, __file);`);
26
+ compiled.push(`__tmp = await __env.run(${file},
27
+ {...__data${data ? `, ${data}` : ""}, content: ${varname}},
28
+ __file
29
+ );
30
+ ${output} += __tmp.content;`);
27
31
  compiled.push("}");
28
32
  return compiled.join("\n");
29
33
  }
@@ -1,7 +1,16 @@
1
1
  import { Token } from "./tokenizer.js";
2
2
  import type { Loader } from "./loader.js";
3
+ export interface TemplateResult {
4
+ content: string;
5
+ [key: string]: unknown;
6
+ }
3
7
  export interface Template {
4
- (data?: Record<string, unknown>): Promise<string>;
8
+ (data?: Record<string, unknown>): Promise<TemplateResult>;
9
+ code: string;
10
+ file?: string;
11
+ }
12
+ export interface TemplateSync {
13
+ (data?: Record<string, unknown>): TemplateResult;
5
14
  code: string;
6
15
  file?: string;
7
16
  }
@@ -20,9 +29,11 @@ export declare class Environment {
20
29
  utils: Record<string, unknown>;
21
30
  constructor(options: Options);
22
31
  use(plugin: Plugin): void;
23
- run(file: string, data: Record<string, unknown>, from?: string): Promise<string>;
24
- runString(source: string, data?: Record<string, unknown>, file?: string): Promise<string>;
25
- compile(source: string, path?: string, defaults?: Record<string, unknown>): Template;
32
+ run(file: string, data: Record<string, unknown>, from?: string): Promise<TemplateResult>;
33
+ runString(source: string, data?: Record<string, unknown>, file?: string): Promise<TemplateResult>;
34
+ runStringSync(source: string, data?: Record<string, unknown>): TemplateResult;
35
+ compile(source: string, path?: string, defaults?: Record<string, unknown>, sync?: false): Template;
36
+ compile(source: string, path?: string, defaults?: Record<string, unknown>, sync?: true): TemplateSync;
26
37
  load(file: string, from?: string): Promise<Template>;
27
38
  compileTokens(tokens: Token[], outputVar?: string, stopAt?: string[]): string[];
28
39
  compileFilters(tokens: Token[], output: string): string;
@@ -29,19 +29,24 @@ export class Environment {
29
29
  const template = this.compile(source, file);
30
30
  return await template(data);
31
31
  }
32
- compile(source, path, defaults) {
32
+ runStringSync(source, data) {
33
+ const template = this.compile(source, "", {}, true);
34
+ return template(data);
35
+ }
36
+ compile(source, path, defaults, sync = false) {
33
37
  try {
34
38
  const tokens = tokenize(source);
35
39
  const code = this.compileTokens(tokens).join("\n");
36
- const constructor = new Function("__file", "__env", "__defaults", `return async function (__data) {
40
+ const constructor = new Function("__file", "__env", "__defaults", `return${sync ? "" : " async"} function (__data) {
37
41
  try {
38
42
  __data = Object.assign({}, __defaults, __data);
39
43
  const ${this.options.dataVarname} = __data;
40
- let __output = "";
44
+ let __tmp;
45
+ const __exports = { content: "" };
41
46
  with (__data) {
42
47
  ${code}
43
48
  }
44
- return __output;
49
+ return __exports;
45
50
  } catch (cause) {
46
51
  throw new Error(\`Error rendering template: \${__file}\`, { cause });
47
52
  }
@@ -66,7 +71,7 @@ export class Environment {
66
71
  }
67
72
  return this.cache.get(file);
68
73
  }
69
- compileTokens(tokens, outputVar = "__output", stopAt) {
74
+ compileTokens(tokens, outputVar = "__exports.content", stopAt) {
70
75
  const compiled = [];
71
76
  tokens: while (tokens.length > 0) {
72
77
  if (stopAt && tokens[0][0] === "tag" && stopAt.includes(tokens[0][1])) {
@@ -0,0 +1 @@
1
+ import "../_dnt.test_polyfills.js";
@@ -0,0 +1 @@
1
+ import "../_dnt.test_polyfills.js";
@@ -9,6 +9,7 @@ export interface TestOptions {
9
9
  includes?: Record<string, string>;
10
10
  }
11
11
  export declare function test(options: TestOptions): Promise<void>;
12
+ export declare function testSync(options: TestOptions): void;
12
13
  export declare class FileLoader implements Loader {
13
14
  files: Record<string, string>;
14
15
  constructor(files: Record<string, string>);
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "module": "./esm/mod.js",
3
3
  "name": "ventojs",
4
- "version": "0.6.0",
4
+ "version": "0.7.1",
5
5
  "description": "🌬 A minimal but powerful template engine",
6
6
  "license": "MIT",
7
7
  "repository": "github:oscarotero/vento",
8
- "homepage": "https://oscarotero.github.io/vento/",
8
+ "homepage": "https://vento.js.org/",
9
9
  "bugs": "https://github.com/oscarotero/vento/issues",
10
10
  "exports": {
11
11
  ".": {