vellora 0.1.0-alpha.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/LICENSE +21 -0
- package/README.md +28 -0
- package/dist/errors.d.ts +74 -0
- package/dist/errors.js +105 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/input.d.ts +6 -0
- package/dist/input.js +79 -0
- package/dist/input.js.map +1 -0
- package/dist/mock-bridge.d.ts +15 -0
- package/dist/mock-bridge.js +0 -0
- package/dist/mock-bridge.js.map +1 -0
- package/dist/native-bridge.d.ts +6 -0
- package/dist/native-bridge.js +43 -0
- package/dist/native-bridge.js.map +1 -0
- package/dist/orchestrate.d.ts +17 -0
- package/dist/orchestrate.js +148 -0
- package/dist/orchestrate.js.map +1 -0
- package/dist/render.d.ts +30 -0
- package/dist/render.js +82 -0
- package/dist/render.js.map +1 -0
- package/dist/source-position.d.ts +10 -0
- package/dist/source-position.js +21 -0
- package/dist/source-position.js.map +1 -0
- package/dist/template/helpers.d.ts +7 -0
- package/dist/template/helpers.js +150 -0
- package/dist/template/helpers.js.map +1 -0
- package/dist/template/index.d.ts +11 -0
- package/dist/template/index.js +11 -0
- package/dist/template/index.js.map +1 -0
- package/dist/template/interpreter.d.ts +4 -0
- package/dist/template/interpreter.js +208 -0
- package/dist/template/interpreter.js.map +1 -0
- package/dist/template/parser.d.ts +22 -0
- package/dist/template/parser.js +99 -0
- package/dist/template/parser.js.map +1 -0
- package/dist/template/tokenizer.d.ts +22 -0
- package/dist/template/tokenizer.js +74 -0
- package/dist/template/tokenizer.js.map +1 -0
- package/dist/types.d.ts +70 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser: build an AST of `text`, `interpolation`, `for`, and `if` nodes from the token stream.
|
|
3
|
+
*
|
|
4
|
+
* Only the documented token syntax is recognized; anything else (unknown tag, unclosed/mismatched
|
|
5
|
+
* block, stray `endfor`/`endif`/`else`) rejects with a located `VelloraTemplateError` before any
|
|
6
|
+
* native call. No arbitrary expression evaluation is parsed — interpolation/conditions are parsed
|
|
7
|
+
* by the interpreter from their raw expression text.
|
|
8
|
+
*/
|
|
9
|
+
import { VelloraTemplateError } from "../errors.js";
|
|
10
|
+
const FOR_RE = /^for\s+([A-Za-z_$][\w$]*)\s+in\s+(.+)$/;
|
|
11
|
+
const IF_RE = /^if\s+(.+)$/;
|
|
12
|
+
export function parse(tokens) {
|
|
13
|
+
const root = [];
|
|
14
|
+
const stack = [];
|
|
15
|
+
const top = () => stack[stack.length - 1]?.current ?? root;
|
|
16
|
+
for (const token of tokens) {
|
|
17
|
+
if (token.kind === "text") {
|
|
18
|
+
top().push({ type: "text", value: token.value });
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (token.kind === "interpolation") {
|
|
22
|
+
top().push({ type: "interpolation", expr: token.value, pos: token.pos });
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
const tag = token.value;
|
|
26
|
+
const forMatch = tag.match(FOR_RE);
|
|
27
|
+
if (forMatch) {
|
|
28
|
+
const item = forMatch[1] ?? "";
|
|
29
|
+
const collection = (forMatch[2] ?? "").trim();
|
|
30
|
+
const frame = {
|
|
31
|
+
kind: "for",
|
|
32
|
+
pos: token.pos,
|
|
33
|
+
current: [],
|
|
34
|
+
build: { type: "for", item, collection },
|
|
35
|
+
};
|
|
36
|
+
stack.push(frame);
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const ifMatch = tag.match(IF_RE);
|
|
40
|
+
if (ifMatch) {
|
|
41
|
+
const frame = {
|
|
42
|
+
kind: "if",
|
|
43
|
+
pos: token.pos,
|
|
44
|
+
current: [],
|
|
45
|
+
build: { type: "if", condition: (ifMatch[1] ?? "").trim(), consequent: [], inElse: false },
|
|
46
|
+
};
|
|
47
|
+
stack.push(frame);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (tag === "else") {
|
|
51
|
+
const frame = stack[stack.length - 1];
|
|
52
|
+
if (!frame || frame.kind !== "if" || frame.build.type !== "if" || frame.build.inElse) {
|
|
53
|
+
throw new VelloraTemplateError("Unexpected {% else %} without an open {% if %}.", token.pos);
|
|
54
|
+
}
|
|
55
|
+
frame.build.consequent = frame.current;
|
|
56
|
+
frame.build.inElse = true;
|
|
57
|
+
frame.current = [];
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (tag === "endfor") {
|
|
61
|
+
const frame = stack.pop();
|
|
62
|
+
if (!frame || frame.kind !== "for" || frame.build.type !== "for") {
|
|
63
|
+
throw new VelloraTemplateError("Unexpected {% endfor %} without an open {% for %}.", token.pos);
|
|
64
|
+
}
|
|
65
|
+
top().push({
|
|
66
|
+
type: "for",
|
|
67
|
+
item: frame.build.item,
|
|
68
|
+
collection: frame.build.collection,
|
|
69
|
+
body: frame.current,
|
|
70
|
+
pos: frame.pos,
|
|
71
|
+
});
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (tag === "endif") {
|
|
75
|
+
const frame = stack.pop();
|
|
76
|
+
if (!frame || frame.kind !== "if" || frame.build.type !== "if") {
|
|
77
|
+
throw new VelloraTemplateError("Unexpected {% endif %} without an open {% if %}.", token.pos);
|
|
78
|
+
}
|
|
79
|
+
const consequent = frame.build.inElse ? frame.build.consequent : frame.current;
|
|
80
|
+
const alternate = frame.build.inElse ? frame.current : [];
|
|
81
|
+
top().push({
|
|
82
|
+
type: "if",
|
|
83
|
+
condition: frame.build.condition,
|
|
84
|
+
consequent,
|
|
85
|
+
alternate,
|
|
86
|
+
pos: frame.pos,
|
|
87
|
+
});
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
throw new VelloraTemplateError(`Unknown template tag: {% ${tag} %}.`, token.pos);
|
|
91
|
+
}
|
|
92
|
+
const unclosed = stack[stack.length - 1];
|
|
93
|
+
if (unclosed) {
|
|
94
|
+
const name = unclosed.kind === "for" ? "{% for %}" : "{% if %}";
|
|
95
|
+
throw new VelloraTemplateError(`Unterminated ${name} block (missing end tag).`, unclosed.pos);
|
|
96
|
+
}
|
|
97
|
+
return root;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/template/parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface Position {
|
|
2
|
+
line: number;
|
|
3
|
+
col: number;
|
|
4
|
+
}
|
|
5
|
+
export type Token = {
|
|
6
|
+
kind: "text";
|
|
7
|
+
value: string;
|
|
8
|
+
pos: Position;
|
|
9
|
+
} | {
|
|
10
|
+
kind: "interpolation";
|
|
11
|
+
value: string;
|
|
12
|
+
pos: Position;
|
|
13
|
+
} | {
|
|
14
|
+
kind: "tag";
|
|
15
|
+
value: string;
|
|
16
|
+
pos: Position;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Single O(N) pass: line/col are tracked incrementally as the cursor advances and snapshotted at
|
|
20
|
+
* each token start, so positions never require rescanning from the start of `source`.
|
|
21
|
+
*/
|
|
22
|
+
export declare function tokenize(source: string): Token[];
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tokenizer: split a template string into `text`, `interpolation` (`{{ }}`), and `tag` (`{% %}`)
|
|
3
|
+
* tokens, tracking 1-based line/column for error reporting. No interpretation happens here.
|
|
4
|
+
*/
|
|
5
|
+
import { VelloraTemplateError } from "../errors.js";
|
|
6
|
+
/**
|
|
7
|
+
* Single O(N) pass: line/col are tracked incrementally as the cursor advances and snapshotted at
|
|
8
|
+
* each token start, so positions never require rescanning from the start of `source`.
|
|
9
|
+
*/
|
|
10
|
+
export function tokenize(source) {
|
|
11
|
+
const tokens = [];
|
|
12
|
+
let i = 0;
|
|
13
|
+
let textStart = 0;
|
|
14
|
+
let line = 1;
|
|
15
|
+
let col = 1;
|
|
16
|
+
// Position of the current pending text run's first char.
|
|
17
|
+
let textStartPos = { line: 1, col: 1 };
|
|
18
|
+
const flushText = (end) => {
|
|
19
|
+
if (end > textStart) {
|
|
20
|
+
tokens.push({
|
|
21
|
+
kind: "text",
|
|
22
|
+
value: source.slice(textStart, end),
|
|
23
|
+
pos: { ...textStartPos },
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
while (i < source.length) {
|
|
28
|
+
const open2 = source.slice(i, i + 2);
|
|
29
|
+
if (open2 === "{{" || open2 === "{%") {
|
|
30
|
+
const isInterp = open2 === "{{";
|
|
31
|
+
const close = isInterp ? "}}" : "%}";
|
|
32
|
+
const closeIdx = source.indexOf(close, i + 2);
|
|
33
|
+
if (closeIdx === -1) {
|
|
34
|
+
throw new VelloraTemplateError(`Unterminated ${isInterp ? "{{ }}" : "{% %}"} tag.`, {
|
|
35
|
+
line,
|
|
36
|
+
col,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
flushText(i);
|
|
40
|
+
const value = source.slice(i + 2, closeIdx).trim();
|
|
41
|
+
tokens.push({
|
|
42
|
+
kind: isInterp ? "interpolation" : "tag",
|
|
43
|
+
value,
|
|
44
|
+
pos: { line, col },
|
|
45
|
+
});
|
|
46
|
+
// Step char-by-char (not a jump) so line/col stay in sync across the tag.
|
|
47
|
+
while (i < closeIdx + 2) {
|
|
48
|
+
if (source[i] === "\n") {
|
|
49
|
+
line++;
|
|
50
|
+
col = 1;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
col++;
|
|
54
|
+
}
|
|
55
|
+
i++;
|
|
56
|
+
}
|
|
57
|
+
textStart = i;
|
|
58
|
+
textStartPos = { line, col };
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
if (source[i] === "\n") {
|
|
62
|
+
line++;
|
|
63
|
+
col = 1;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
col++;
|
|
67
|
+
}
|
|
68
|
+
i++;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
flushText(source.length);
|
|
72
|
+
return tokens;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=tokenizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenizer.js","sourceRoot":"","sources":["../../src/template/tokenizer.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public option/data types and the swappable native-bridge contract.
|
|
3
|
+
*
|
|
4
|
+
* `NativeBridge` is the single, narrow boundary the orchestration calls to render. A mock backs all
|
|
5
|
+
* current tests; `@vellora/native` provides the real, drop-in implementation once
|
|
6
|
+
* the napi binding lands. The interface matches that eventual contract: async, content +
|
|
7
|
+
* resolved options in, PDF bytes out.
|
|
8
|
+
*/
|
|
9
|
+
import type { Readable } from "node:stream";
|
|
10
|
+
/** Accepted `html` input. Always document **content**, never a filesystem path. */
|
|
11
|
+
export type HtmlInput = string | Uint8Array | Readable;
|
|
12
|
+
/** Render `data` for the templating engine: an arbitrary plain object of values. */
|
|
13
|
+
export type RenderData = Record<string, unknown>;
|
|
14
|
+
/**
|
|
15
|
+
* Document metadata forwarded to the core. Currently, `title` and `creationDate` are honored.
|
|
16
|
+
* `creationDate` is an ISO-8601 string so identical inputs serialize byte-identically.
|
|
17
|
+
*/
|
|
18
|
+
export interface RenderMetadata {
|
|
19
|
+
title?: string;
|
|
20
|
+
/**
|
|
21
|
+
* ISO-8601 instant recorded as the PDF creation date. Must be `Date`-parseable; an empty or
|
|
22
|
+
* non-parseable value rejects with `VelloraInputError`. Omitted ⇒ a fixed deterministic default.
|
|
23
|
+
*
|
|
24
|
+
* Currently records **date granularity only — (year, month, day) in UTC** — because `vellora-core`
|
|
25
|
+
* accepts only y/m/d; the time-of-day and timezone are intentionally dropped (the recorded
|
|
26
|
+
* `/CreationDate` is always at 00:00:00 UTC).
|
|
27
|
+
*/
|
|
28
|
+
creationDate?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Public render options. `opts` is the single carrier for render configuration and is forwarded to
|
|
32
|
+
* the orchestration and native layers. Currently only `metadata` has a rendering effect;
|
|
33
|
+
* `fonts`/`images`/`baseUrl` are accepted and forwarded but will take effect in a future release —
|
|
34
|
+
* forwarding them MUST NOT change current output.
|
|
35
|
+
*/
|
|
36
|
+
export interface RenderOptions {
|
|
37
|
+
/** Strict-by-default: validate, never mutate. `false` runs `@vellora/lint` fixers first. */
|
|
38
|
+
strict?: boolean;
|
|
39
|
+
/** Document metadata (title, creation date). */
|
|
40
|
+
metadata?: RenderMetadata;
|
|
41
|
+
/** Planned: explicit fonts. Forwarded but currently inert. */
|
|
42
|
+
fonts?: unknown;
|
|
43
|
+
/** Planned: explicit images. Forwarded but currently inert. */
|
|
44
|
+
images?: unknown;
|
|
45
|
+
/** Planned: base URL for relative assets. Forwarded but currently inert. */
|
|
46
|
+
baseUrl?: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The fully-resolved configuration handed to the bridge. Derived from `RenderOptions` with the
|
|
50
|
+
* deterministic creation-date default applied. This is what the native addon receives.
|
|
51
|
+
*/
|
|
52
|
+
export interface BridgeRenderOptions {
|
|
53
|
+
metadata: RenderMetadata & {
|
|
54
|
+
creationDate: string;
|
|
55
|
+
};
|
|
56
|
+
fonts?: unknown;
|
|
57
|
+
images?: unknown;
|
|
58
|
+
baseUrl?: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The swappable native render boundary. The real `@vellora/native` implements this exact type, so
|
|
62
|
+
* the mock is a drop-in: async, finalized HTML + resolved options in, complete PDF bytes out.
|
|
63
|
+
*
|
|
64
|
+
* Out-of-subset input is signaled by a **rejection** carrying the structured located diagnostic
|
|
65
|
+
* `{ feature, line, col, hint }` (see `UnsupportedDiagnostic`), which the orchestration maps to a
|
|
66
|
+
* `VelloraUnsupportedError`.
|
|
67
|
+
*/
|
|
68
|
+
export interface NativeBridge {
|
|
69
|
+
render(html: string, options: BridgeRenderOptions): Promise<Uint8Array>;
|
|
70
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vellora",
|
|
3
|
+
"version": "0.1.0-alpha.0",
|
|
4
|
+
"description": "HTML to PDF for Node.js — no browser. Public API + templating + strict orchestration.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"html-to-pdf",
|
|
7
|
+
"pdf",
|
|
8
|
+
"pdf-generation",
|
|
9
|
+
"invoice",
|
|
10
|
+
"boleto",
|
|
11
|
+
"receipt",
|
|
12
|
+
"pdfa",
|
|
13
|
+
"pdfua",
|
|
14
|
+
"tagged-pdf",
|
|
15
|
+
"no-puppeteer",
|
|
16
|
+
"no-chromium",
|
|
17
|
+
"serverless",
|
|
18
|
+
"wkhtmltopdf-alternative",
|
|
19
|
+
"napi-rs",
|
|
20
|
+
"nodejs"
|
|
21
|
+
],
|
|
22
|
+
"homepage": "https://github.com/diomalta/vellora#readme",
|
|
23
|
+
"bugs": "https://github.com/diomalta/vellora/issues",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/diomalta/vellora.git",
|
|
27
|
+
"directory": "packages/vellora"
|
|
28
|
+
},
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"author": "Diego Malta <diohmalta@gmail.com>",
|
|
31
|
+
"type": "module",
|
|
32
|
+
"main": "dist/index.js",
|
|
33
|
+
"types": "dist/index.d.ts",
|
|
34
|
+
"exports": {
|
|
35
|
+
".": {
|
|
36
|
+
"types": "./dist/index.d.ts",
|
|
37
|
+
"import": "./dist/index.js"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"sideEffects": false,
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=20"
|
|
43
|
+
},
|
|
44
|
+
"files": ["dist", "README.md", "LICENSE"],
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "tsc"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@vellora/lint": "0.1.0-alpha.0",
|
|
50
|
+
"@vellora/native": "0.1.0-alpha.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@vellora/test-harness": "*",
|
|
54
|
+
"esbuild": "^0.21.5"
|
|
55
|
+
},
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public",
|
|
58
|
+
"provenance": true
|
|
59
|
+
}
|
|
60
|
+
}
|