@quillmark/quiver 0.16.0 → 0.17.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 +201 -21
- package/NOTICE +2 -0
- package/README.md +84 -245
- package/dist/bin/quiver.d.ts +4 -4
- package/dist/bin/quiver.js +28 -60
- package/dist/build.d.ts +12 -12
- package/dist/build.js +74 -31
- package/dist/built-loader.d.ts +16 -16
- package/dist/built-loader.js +115 -113
- package/dist/bundle.js +1 -1
- package/dist/digest.d.ts +19 -0
- package/dist/digest.js +25 -0
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +4 -5
- package/dist/node.d.ts +47 -76
- package/dist/node.js +59 -64
- package/dist/quiver-yaml.js +19 -19
- package/dist/quiver.d.ts +25 -37
- package/dist/quiver.js +46 -113
- package/dist/ref.d.ts +3 -3
- package/dist/ref.js +15 -13
- package/dist/semver.d.ts +5 -1
- package/dist/semver.js +9 -10
- package/dist/source-loader.d.ts +3 -3
- package/dist/source-loader.js +26 -26
- package/dist/transports/fs-built-transport.d.ts +2 -2
- package/dist/transports/fs-built-transport.js +10 -8
- package/dist/transports/http-transport.d.ts +2 -2
- package/dist/transports/http-transport.js +11 -9
- package/package.json +66 -72
- package/dist/preview.d.ts +0 -81
- package/dist/preview.js +0 -208
- package/dist/testing.d.ts +0 -28
- package/dist/testing.js +0 -66
package/dist/preview.js
DELETED
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Manual-validation helper for Quiver authors.
|
|
3
|
-
*
|
|
4
|
-
* `runQuiverTests` (`@quillmark/quiver/testing`) proves every quill *compiles*
|
|
5
|
-
* — it never produces a rendered artifact a human can look at. This module
|
|
6
|
-
* closes that gap: it renders each quill's example document, writes the
|
|
7
|
-
* artifacts to a directory, and emits an `index.html` gallery so an author can
|
|
8
|
-
* eyeball real output before publishing.
|
|
9
|
-
*
|
|
10
|
-
* Node-only: writes files and loads a source quiver from disk.
|
|
11
|
-
*
|
|
12
|
-
* Usage (place a script next to your Quiver.yaml):
|
|
13
|
-
*
|
|
14
|
-
* import { Engine } from "@quillmark/wasm";
|
|
15
|
-
* import { renderQuiverSamples } from "@quillmark/quiver/preview";
|
|
16
|
-
*
|
|
17
|
-
* await renderQuiverSamples(import.meta.url, { engine: new Engine() });
|
|
18
|
-
* // → open ./preview/index.html
|
|
19
|
-
*
|
|
20
|
-
* The sample document is the illustrative example seeded by
|
|
21
|
-
* `quill.seedDocument()` for each quill version — a fully filled-out,
|
|
22
|
-
* always-renderable document (the blueprint itself carries `<must-fill>`
|
|
23
|
-
* sentinels and is not renderable). Every quill always seeds an example, so no
|
|
24
|
-
* quills are skipped for lack of a sample document.
|
|
25
|
-
*
|
|
26
|
-
* A `.gitignore` is written into `outDir` so the generated artifacts are not
|
|
27
|
-
* accidentally committed.
|
|
28
|
-
*/
|
|
29
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
30
|
-
import { join } from "node:path";
|
|
31
|
-
import { Quiver } from "./node.js";
|
|
32
|
-
import { isQuillmarkError } from "@quillmark/wasm";
|
|
33
|
-
/** Default directory rendered artifacts are written to. */
|
|
34
|
-
const DEFAULT_OUT_DIR = "preview";
|
|
35
|
-
/**
|
|
36
|
-
* Renders every quill's example document and writes the artifacts plus an
|
|
37
|
-
* `index.html` gallery to `outDir`.
|
|
38
|
-
*
|
|
39
|
-
* Does NOT fail fast: a quill that throws is recorded as `failed` and the
|
|
40
|
-
* run continues, so one broken quill never hides the others. Inspect the
|
|
41
|
-
* returned array (or `index.html`) for the full picture.
|
|
42
|
-
*
|
|
43
|
-
* @param metaUrlOrDir `import.meta.url` when called from the quiver root, or
|
|
44
|
-
* an absolute path to the source quiver directory.
|
|
45
|
-
*/
|
|
46
|
-
export async function renderQuiverSamples(metaUrlOrDir, opts) {
|
|
47
|
-
const outDir = opts.outDir ?? DEFAULT_OUT_DIR;
|
|
48
|
-
const quiver = await Quiver.fromDir(metaUrlOrDir);
|
|
49
|
-
await mkdir(outDir, { recursive: true });
|
|
50
|
-
const results = [];
|
|
51
|
-
for (const name of quiver.quillNames()) {
|
|
52
|
-
for (const version of quiver.versionsOf(name)) {
|
|
53
|
-
if (!isSelected(name, `${name}@${version}`, opts))
|
|
54
|
-
continue;
|
|
55
|
-
results.push(await renderOne(quiver, name, version, outDir, opts));
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
await writeFile(join(outDir, ".gitignore"), "*\n");
|
|
59
|
-
await writeFile(join(outDir, "index.html"), renderIndexHtml(quiver.name, results));
|
|
60
|
-
if (!opts.quiet)
|
|
61
|
-
printSummary(quiver.name, outDir, results);
|
|
62
|
-
return results;
|
|
63
|
-
}
|
|
64
|
-
/** Whether a quill passes the `include`/`exclude` filters. */
|
|
65
|
-
function isSelected(name, ref, opts) {
|
|
66
|
-
const matches = (list) => list.includes(name) || list.includes(ref);
|
|
67
|
-
if (opts.include && !matches(opts.include))
|
|
68
|
-
return false;
|
|
69
|
-
if (opts.exclude && matches(opts.exclude))
|
|
70
|
-
return false;
|
|
71
|
-
return true;
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Formats a thrown render error into one string per diagnostic. `@quillmark/wasm`
|
|
75
|
-
* throws `QuillmarkError`s (an `Error` carrying `diagnostics`); fall back to
|
|
76
|
-
* the message for anything else.
|
|
77
|
-
*/
|
|
78
|
-
function failureReasons(err) {
|
|
79
|
-
if (isQuillmarkError(err) && err.diagnostics.length > 0) {
|
|
80
|
-
return err.diagnostics.map((d) => `${d.severity}: ${d.message}`);
|
|
81
|
-
}
|
|
82
|
-
return [err.message];
|
|
83
|
-
}
|
|
84
|
-
async function renderOne(quiver, name, version, outDir, opts) {
|
|
85
|
-
const ref = `${name}@${version}`;
|
|
86
|
-
let result;
|
|
87
|
-
try {
|
|
88
|
-
// Canonical render path: a core Quill + its seeded Document handed straight
|
|
89
|
-
// to the Engine, which clones them into the backend and frees the clones.
|
|
90
|
-
const quill = await quiver.getQuill(ref);
|
|
91
|
-
const doc = quill.seedDocument();
|
|
92
|
-
try {
|
|
93
|
-
result = await opts.engine.render(quill, doc, opts.format ? { format: opts.format } : undefined);
|
|
94
|
-
}
|
|
95
|
-
finally {
|
|
96
|
-
doc.free();
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
catch (err) {
|
|
100
|
-
return {
|
|
101
|
-
ref,
|
|
102
|
-
status: "failed",
|
|
103
|
-
files: [],
|
|
104
|
-
warnings: [],
|
|
105
|
-
reasons: failureReasons(err),
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
const warnings = (result.warnings ?? []).map((w) => `${w.severity}: ${w.message}`);
|
|
109
|
-
const artifacts = result.artifacts ?? [];
|
|
110
|
-
if (artifacts.length === 0) {
|
|
111
|
-
return {
|
|
112
|
-
ref,
|
|
113
|
-
status: "failed",
|
|
114
|
-
files: [],
|
|
115
|
-
warnings,
|
|
116
|
-
reasons: ["render produced no artifacts"],
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
const files = [];
|
|
120
|
-
for (let i = 0; i < artifacts.length; i++) {
|
|
121
|
-
const artifact = artifacts[i];
|
|
122
|
-
const suffix = artifacts.length > 1 ? `.${i}` : "";
|
|
123
|
-
const fileName = `${ref}${suffix}.${artifact.format}`;
|
|
124
|
-
await writeFile(join(outDir, fileName), artifact.bytes);
|
|
125
|
-
files.push(fileName);
|
|
126
|
-
}
|
|
127
|
-
return { ref, status: "rendered", files, warnings, reasons: [] };
|
|
128
|
-
}
|
|
129
|
-
function printSummary(quiverName, outDir, results) {
|
|
130
|
-
const count = (s) => results.filter((r) => r.status === s).length;
|
|
131
|
-
console.log(`\nQuiver "${quiverName}" — sample render`);
|
|
132
|
-
for (const r of results) {
|
|
133
|
-
const detail = r.status === "rendered" ? r.files.join(", ") : (r.reasons[0] ?? "");
|
|
134
|
-
console.log(` [${r.status.padEnd(8)}] ${r.ref}${detail ? ` — ${detail}` : ""}`);
|
|
135
|
-
for (const extra of r.reasons.slice(1))
|
|
136
|
-
console.log(` ${extra}`);
|
|
137
|
-
for (const w of r.warnings)
|
|
138
|
-
console.log(` ⚠ ${w}`);
|
|
139
|
-
}
|
|
140
|
-
console.log(`\n${count("rendered")} rendered, ${count("failed")} failed`);
|
|
141
|
-
console.log(`Open ${join(outDir, "index.html")} to review.\n`);
|
|
142
|
-
}
|
|
143
|
-
function escapeHtml(text) {
|
|
144
|
-
return text
|
|
145
|
-
.replace(/&/g, "&")
|
|
146
|
-
.replace(/</g, "<")
|
|
147
|
-
.replace(/>/g, ">")
|
|
148
|
-
.replace(/"/g, """);
|
|
149
|
-
}
|
|
150
|
-
function embedArtifact(fileName) {
|
|
151
|
-
const ext = fileName.slice(fileName.lastIndexOf(".") + 1).toLowerCase();
|
|
152
|
-
const src = escapeHtml(fileName);
|
|
153
|
-
if (ext === "pdf") {
|
|
154
|
-
return `<iframe class="art" src="${src}" title="${src}"></iframe>`;
|
|
155
|
-
}
|
|
156
|
-
if (ext === "png" || ext === "svg") {
|
|
157
|
-
return `<img class="art" src="${src}" alt="${src}" />`;
|
|
158
|
-
}
|
|
159
|
-
return `<a href="${src}">${src}</a>`;
|
|
160
|
-
}
|
|
161
|
-
function renderIndexHtml(quiverName, results) {
|
|
162
|
-
const cards = results
|
|
163
|
-
.map((r) => {
|
|
164
|
-
const body = r.status === "rendered"
|
|
165
|
-
? r.files.map(embedArtifact).join("\n")
|
|
166
|
-
: `<ul class="reasons">${r.reasons
|
|
167
|
-
.map((reason) => `<li>${escapeHtml(reason)}</li>`)
|
|
168
|
-
.join("")}</ul>`;
|
|
169
|
-
const warnings = r.warnings.length
|
|
170
|
-
? `<ul class="warnings">${r.warnings
|
|
171
|
-
.map((w) => `<li>${escapeHtml(w)}</li>`)
|
|
172
|
-
.join("")}</ul>`
|
|
173
|
-
: "";
|
|
174
|
-
return `<section class="card ${r.status}">
|
|
175
|
-
<h2>${escapeHtml(r.ref)} <span class="badge">${r.status}</span></h2>
|
|
176
|
-
${warnings}
|
|
177
|
-
${body}
|
|
178
|
-
</section>`;
|
|
179
|
-
})
|
|
180
|
-
.join("\n");
|
|
181
|
-
return `<!doctype html>
|
|
182
|
-
<html lang="en">
|
|
183
|
-
<head>
|
|
184
|
-
<meta charset="utf-8" />
|
|
185
|
-
<title>Quiver preview — ${escapeHtml(quiverName)}</title>
|
|
186
|
-
<style>
|
|
187
|
-
body { font-family: system-ui, sans-serif; margin: 2rem; background: #fafafa; }
|
|
188
|
-
h1 { font-size: 1.4rem; }
|
|
189
|
-
.card { background: #fff; border: 1px solid #ddd; border-radius: 8px;
|
|
190
|
-
padding: 1rem; margin: 1rem 0; }
|
|
191
|
-
.card.failed { border-color: #e0b4b4; }
|
|
192
|
-
h2 { font-size: 1rem; margin: 0 0 0.5rem; }
|
|
193
|
-
.badge { font-size: 0.7rem; text-transform: uppercase; background: #eee;
|
|
194
|
-
border-radius: 4px; padding: 2px 6px; vertical-align: middle; }
|
|
195
|
-
.failed .badge { background: #f3d2d2; }
|
|
196
|
-
.art { width: 100%; height: 600px; border: 1px solid #eee; }
|
|
197
|
-
img.art { height: auto; }
|
|
198
|
-
.reasons { color: #a33; font-style: italic; }
|
|
199
|
-
.warnings { color: #96690a; font-size: 0.85rem; }
|
|
200
|
-
</style>
|
|
201
|
-
</head>
|
|
202
|
-
<body>
|
|
203
|
-
<h1>Quiver preview — ${escapeHtml(quiverName)}</h1>
|
|
204
|
-
${cards}
|
|
205
|
-
</body>
|
|
206
|
-
</html>
|
|
207
|
-
`;
|
|
208
|
-
}
|
package/dist/testing.d.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Convenience test harness for Quiver authors using `node:test`.
|
|
3
|
-
*
|
|
4
|
-
* Built into Node 18+; no extra test-runner dependency required. If you
|
|
5
|
-
* prefer vitest, jest, or another runner, write a 12-line loop against
|
|
6
|
-
* the main API instead — every primitive used here is public.
|
|
7
|
-
*
|
|
8
|
-
* Usage (place this file next to your Quiver.yaml):
|
|
9
|
-
*
|
|
10
|
-
* import { Engine } from "@quillmark/wasm";
|
|
11
|
-
* import { runQuiverTests } from "@quillmark/quiver/testing";
|
|
12
|
-
* runQuiverTests(import.meta.url, new Engine());
|
|
13
|
-
*
|
|
14
|
-
* Run with `node --test`.
|
|
15
|
-
*/
|
|
16
|
-
import type { Engine } from "@quillmark/wasm";
|
|
17
|
-
/**
|
|
18
|
-
* Registers a `node:test` describe block that validates every quill
|
|
19
|
-
* version in the quiver at `metaUrlOrDir` against the provided engine.
|
|
20
|
-
*
|
|
21
|
-
* Pass `import.meta.url` when this file lives at the quiver root (next
|
|
22
|
-
* to Quiver.yaml). Pass an absolute directory path for any other layout.
|
|
23
|
-
*
|
|
24
|
-
* Validation covers the full loading pipeline: Quiver.yaml, Quill.yaml,
|
|
25
|
-
* all template files, quill construction via Quill.fromTree(tree), and a
|
|
26
|
-
* full render of each quill's example document via engine.render(quill, doc).
|
|
27
|
-
*/
|
|
28
|
-
export declare function runQuiverTests(metaUrlOrDir: string, engine: Engine): void;
|
package/dist/testing.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Convenience test harness for Quiver authors using `node:test`.
|
|
3
|
-
*
|
|
4
|
-
* Built into Node 18+; no extra test-runner dependency required. If you
|
|
5
|
-
* prefer vitest, jest, or another runner, write a 12-line loop against
|
|
6
|
-
* the main API instead — every primitive used here is public.
|
|
7
|
-
*
|
|
8
|
-
* Usage (place this file next to your Quiver.yaml):
|
|
9
|
-
*
|
|
10
|
-
* import { Engine } from "@quillmark/wasm";
|
|
11
|
-
* import { runQuiverTests } from "@quillmark/quiver/testing";
|
|
12
|
-
* runQuiverTests(import.meta.url, new Engine());
|
|
13
|
-
*
|
|
14
|
-
* Run with `node --test`.
|
|
15
|
-
*/
|
|
16
|
-
import { describe, it, before } from "node:test";
|
|
17
|
-
// Import from the Node entry: this installs the runtime patch so
|
|
18
|
-
// `Quiver.fromDir` is callable at runtime, and gives us the augmented
|
|
19
|
-
// static-method type signature.
|
|
20
|
-
import { Quiver } from "./node.js";
|
|
21
|
-
/**
|
|
22
|
-
* Registers a `node:test` describe block that validates every quill
|
|
23
|
-
* version in the quiver at `metaUrlOrDir` against the provided engine.
|
|
24
|
-
*
|
|
25
|
-
* Pass `import.meta.url` when this file lives at the quiver root (next
|
|
26
|
-
* to Quiver.yaml). Pass an absolute directory path for any other layout.
|
|
27
|
-
*
|
|
28
|
-
* Validation covers the full loading pipeline: Quiver.yaml, Quill.yaml,
|
|
29
|
-
* all template files, quill construction via Quill.fromTree(tree), and a
|
|
30
|
-
* full render of each quill's example document via engine.render(quill, doc).
|
|
31
|
-
*/
|
|
32
|
-
export function runQuiverTests(metaUrlOrDir, engine) {
|
|
33
|
-
describe("Quiver", () => {
|
|
34
|
-
let quiver;
|
|
35
|
-
before(async () => {
|
|
36
|
-
quiver = await Quiver.fromDir(metaUrlOrDir);
|
|
37
|
-
});
|
|
38
|
-
it("has at least one quill", () => {
|
|
39
|
-
if (quiver.quillNames().length === 0) {
|
|
40
|
-
throw new Error("Quiver has no quills");
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
it("compiles and renders every quill's example without error", async () => {
|
|
44
|
-
for (const name of quiver.quillNames()) {
|
|
45
|
-
for (const version of quiver.versionsOf(name)) {
|
|
46
|
-
const ref = `${name}@${version}`;
|
|
47
|
-
// The Engine takes the core Quill + its seeded Document directly and
|
|
48
|
-
// clones them into the backend; the doc clone is freed inside render,
|
|
49
|
-
// and we free our own core Document here.
|
|
50
|
-
const quill = await quiver.getQuill(ref);
|
|
51
|
-
const doc = quill.seedDocument();
|
|
52
|
-
let result;
|
|
53
|
-
try {
|
|
54
|
-
result = await engine.render(quill, doc);
|
|
55
|
-
}
|
|
56
|
-
finally {
|
|
57
|
-
doc.free();
|
|
58
|
-
}
|
|
59
|
-
if (!Array.isArray(result.artifacts) || result.artifacts.length === 0) {
|
|
60
|
-
throw new Error(`${ref}: example render produced no artifacts`);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
}
|