gutterpress 0.11.15-alpha.2 → 0.11.16-alpha.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 +6 -0
- package/dist/{README-8b33qbjw.md → README-vsnsc1b3.md} +14 -0
- package/dist/api/index.d.ts +2 -2
- package/dist/api/index.js +4 -6
- package/dist/{build-4522yqmm.js → build-46bb205t.js} +4 -4
- package/dist/{cli-b496rxy0.js → cli-6tfc4ed6.js} +1 -1
- package/dist/{cli-mc3waxaw.js → cli-d55z7rtk.js} +3 -3
- package/dist/{cli-vdpnctkq.js → cli-p8xwsknv.js} +1 -1
- package/dist/{cli-7pw53gnm.js → cli-sq0awkbf.js} +161 -7
- package/dist/cli.js +12 -12
- package/dist/{doctor-fgvwe9j2.js → doctor-tjatz81j.js} +2 -2
- package/dist/{engine-h8zdtxhp.js → engine-nv2xddnk.js} +1 -1
- package/dist/{engine-s18wy689.js → engine-szx34415.js} +2 -2
- package/dist/{ext-v44phkxz.js → ext-ph0770mx.js} +4 -4
- package/dist/{index-ypxhxvwv.js → index-9ydc9q9q.js} +1 -1
- package/dist/{index-j77039xr.js → index-azdfgy3t.js} +245 -53
- package/dist/{index-w6tqhafk.js → index-eawjxgkn.js} +3 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -7
- package/dist/lib/markdown/assemble.d.ts +7 -0
- package/dist/lib/markdown/plugins.d.ts +1 -1
- package/dist/lib/markdown/renderer.d.ts +91 -0
- package/dist/lib/snippets.d.ts +34 -92
- package/dist/{new-7npm260n.js → new-daeh9p9p.js} +4 -4
- package/dist/{plugin.js-aszz0zgh.tpl → plugin.js-z492w01r.tpl} +44 -0
- package/dist/{plugin.test.js-nw7c84s6.tpl → plugin.test.js-6n8ct5e3.tpl} +35 -1
- package/dist/{preflight-y0jhsz8p.js → preflight-bfvw2qe5.js} +4 -4
- package/dist/{preview-xkp190pd.js → preview-00298rvf.js} +4 -4
- package/dist/{publish-czx47htf.js → publish-jn9t18mr.js} +4 -4
- package/dist/render.js +156 -2
- package/dist/{validate-x89z3zrq.js → validate-cm1m5bbf.js} +4 -4
- package/package.json +1 -1
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
MARGIN_BOX_IGNORED_PROPERTIES,
|
|
5
5
|
getAssetPath,
|
|
6
6
|
getAssetsDir
|
|
7
|
-
} from "./index-
|
|
7
|
+
} from "./index-eawjxgkn.js";
|
|
8
8
|
import {
|
|
9
9
|
INSTALL_HINTS,
|
|
10
10
|
execCapture,
|
|
@@ -855,6 +855,133 @@ function warn(env, line, type, message, marker) {
|
|
|
855
855
|
env.layoutWarnings = [];
|
|
856
856
|
env.layoutWarnings.push({ line, type, message, marker });
|
|
857
857
|
}
|
|
858
|
+
var COMPONENT_SEVERITIES = ["error", "warning", "info"];
|
|
859
|
+
function blockEnd(tokens, i) {
|
|
860
|
+
if (tokens[i].nesting !== 1)
|
|
861
|
+
return i;
|
|
862
|
+
let depth = 0;
|
|
863
|
+
for (let j = i;j < tokens.length; j++) {
|
|
864
|
+
depth += tokens[j].nesting;
|
|
865
|
+
if (depth === 0)
|
|
866
|
+
return j;
|
|
867
|
+
}
|
|
868
|
+
return tokens.length - 1;
|
|
869
|
+
}
|
|
870
|
+
function inlineText(tokens, from, to) {
|
|
871
|
+
const parts = [];
|
|
872
|
+
for (let k = from;k <= to; k++)
|
|
873
|
+
if (tokens[k].type === "inline")
|
|
874
|
+
parts.push(tokens[k].content);
|
|
875
|
+
return parts.join(`
|
|
876
|
+
`);
|
|
877
|
+
}
|
|
878
|
+
function soleImage(inline) {
|
|
879
|
+
const parts = (inline && inline.children || []).filter((c) => c.type !== "softbreak" && !(c.type === "text" && !c.content.trim()));
|
|
880
|
+
return parts.length === 1 && parts[0].type === "image" ? parts[0] : null;
|
|
881
|
+
}
|
|
882
|
+
function toComponentBlocks(tokens, fallbackLine) {
|
|
883
|
+
const blocks = [];
|
|
884
|
+
for (let i = 0;i < tokens.length; i++) {
|
|
885
|
+
const tok = tokens[i];
|
|
886
|
+
const end = blockEnd(tokens, i);
|
|
887
|
+
const line = tok.map ? tok.map[0] + 1 : tok.meta && tok.meta.line || fallbackLine;
|
|
888
|
+
switch (tok.type) {
|
|
889
|
+
case "heading_open":
|
|
890
|
+
blocks.push({ type: "heading", level: Number(tok.tag.slice(1)), text: inlineText(tokens, i, end), line });
|
|
891
|
+
break;
|
|
892
|
+
case "paragraph_open": {
|
|
893
|
+
const image = soleImage(tokens[i + 1]);
|
|
894
|
+
blocks.push(image ? { type: "image", alt: image.content, src: image.attrGet("src") || "", line } : { type: "paragraph", text: inlineText(tokens, i, end), line });
|
|
895
|
+
break;
|
|
896
|
+
}
|
|
897
|
+
case "bullet_list_open":
|
|
898
|
+
case "ordered_list_open": {
|
|
899
|
+
const items = [];
|
|
900
|
+
for (let k = i + 1;k < end; k++) {
|
|
901
|
+
if (tokens[k].type !== "list_item_open" || tokens[k].level !== tok.level + 1)
|
|
902
|
+
continue;
|
|
903
|
+
const itemEnd = blockEnd(tokens, k);
|
|
904
|
+
const first = tokens.slice(k + 1, itemEnd).find((t) => t.type === "inline");
|
|
905
|
+
items.push(first ? first.content : "");
|
|
906
|
+
k = itemEnd;
|
|
907
|
+
}
|
|
908
|
+
blocks.push({ type: "list", ordered: tok.type === "ordered_list_open", items, line });
|
|
909
|
+
break;
|
|
910
|
+
}
|
|
911
|
+
case "blockquote_open":
|
|
912
|
+
blocks.push({ type: "quote", text: inlineText(tokens, i, end), line });
|
|
913
|
+
break;
|
|
914
|
+
case "fence":
|
|
915
|
+
case "code_block":
|
|
916
|
+
blocks.push({ type: "code", lang: (tok.info || "").trim().split(/\s+/)[0] || "", text: tok.content, line });
|
|
917
|
+
break;
|
|
918
|
+
case "table_open":
|
|
919
|
+
blocks.push({ type: "table", line });
|
|
920
|
+
break;
|
|
921
|
+
case "hr":
|
|
922
|
+
blocks.push({ type: "rule", line });
|
|
923
|
+
break;
|
|
924
|
+
case "html_block":
|
|
925
|
+
blocks.push({ type: "html", text: tok.content, line });
|
|
926
|
+
break;
|
|
927
|
+
case "layout_component_open":
|
|
928
|
+
blocks.push({ type: "component", name: tok.meta.component, line });
|
|
929
|
+
break;
|
|
930
|
+
default:
|
|
931
|
+
blocks.push({ type: tok.type.replace(/_open$/, ""), line });
|
|
932
|
+
}
|
|
933
|
+
i = end;
|
|
934
|
+
}
|
|
935
|
+
return blocks;
|
|
936
|
+
}
|
|
937
|
+
function runComponentValidate(env, decl, meta, tokens, lines) {
|
|
938
|
+
const name = meta.component;
|
|
939
|
+
const line = meta.line || 0;
|
|
940
|
+
const report = (at, type, message, severity) => {
|
|
941
|
+
if (!env.layoutWarnings)
|
|
942
|
+
env.layoutWarnings = [];
|
|
943
|
+
env.layoutWarnings.push(severity ? { line: at, type, message, severity } : { line: at, type, message });
|
|
944
|
+
};
|
|
945
|
+
const failed = (why) => report(line, "component_validate_failed", `@${name}: plugin "${decl.validateOwner}"'s validate() ${why}`);
|
|
946
|
+
let lastLine = line;
|
|
947
|
+
for (const t of tokens)
|
|
948
|
+
if (t.map && t.map[1] > lastLine)
|
|
949
|
+
lastLine = t.map[1];
|
|
950
|
+
const component = {
|
|
951
|
+
name,
|
|
952
|
+
variant: meta.variant || null,
|
|
953
|
+
attrs: { ...meta.attrs },
|
|
954
|
+
line,
|
|
955
|
+
text: lines.slice(line, lastLine).join(`
|
|
956
|
+
`),
|
|
957
|
+
blocks: toComponentBlocks(tokens, line)
|
|
958
|
+
};
|
|
959
|
+
let result;
|
|
960
|
+
try {
|
|
961
|
+
result = decl.validate(component);
|
|
962
|
+
} catch (error) {
|
|
963
|
+
failed(`threw: ${error instanceof Error ? error.message : String(error)}`);
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
if (result === undefined || result === null)
|
|
967
|
+
return;
|
|
968
|
+
if (typeof result === "string")
|
|
969
|
+
result = [result];
|
|
970
|
+
if (!Array.isArray(result)) {
|
|
971
|
+
failed("must return a message, an array of problems, or nothing (and must not be async).");
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
974
|
+
for (const entry of result) {
|
|
975
|
+
const problem = typeof entry === "string" ? { message: entry } : entry;
|
|
976
|
+
if (!problem || typeof problem.message !== "string" || !problem.message.trim()) {
|
|
977
|
+
failed("returned a problem without a message.");
|
|
978
|
+
continue;
|
|
979
|
+
}
|
|
980
|
+
const at = Number.isInteger(problem.line) && problem.line > 0 ? problem.line : line;
|
|
981
|
+
const severity = COMPONENT_SEVERITIES.includes(problem.severity) ? problem.severity : undefined;
|
|
982
|
+
report(at, "component_invalid", `@${name}: ${problem.message}`, severity);
|
|
983
|
+
}
|
|
984
|
+
}
|
|
858
985
|
function proseEscapeHint(kind) {
|
|
859
986
|
return ` If this line is prose that happens to begin with "@${kind}" — a wrapped sentence, for` + ` instance — escape it as \\@${kind} or write it as \`@${kind}\` in backticks, and it stays text.`;
|
|
860
987
|
}
|
|
@@ -957,13 +1084,20 @@ function resolveContainerShape(name, pluginName, decl) {
|
|
|
957
1084
|
}
|
|
958
1085
|
autoCloseAtEof = decl.autoCloseAt.includes("eof");
|
|
959
1086
|
}
|
|
960
|
-
|
|
1087
|
+
if (decl.validate !== undefined && typeof decl.validate !== "function") {
|
|
1088
|
+
throw new Error(`Plugin "${pluginName}"'s marker "@${name}" has a \`validate\` that is not a function.`);
|
|
1089
|
+
}
|
|
1090
|
+
const check = decl.validate ? { validate: decl.validate, validateOwner: pluginName } : {};
|
|
1091
|
+
return { tag, classBase, variants, label, autoCloseAtEof, ...check };
|
|
961
1092
|
}
|
|
962
1093
|
function resolveMarkerDeclaration(name, rawDecl, rawRegistry, originOf) {
|
|
963
1094
|
const pluginName = originOf.get(name);
|
|
964
1095
|
if (typeof rawDecl !== "object" || rawDecl === null || Array.isArray(rawDecl)) {
|
|
965
1096
|
throw new Error(`Plugin "${pluginName}"'s marker "@${name}" is not a plain object.`);
|
|
966
1097
|
}
|
|
1098
|
+
if (rawDecl.snippet !== undefined && (typeof rawDecl.snippet !== "string" || !rawDecl.snippet.trim())) {
|
|
1099
|
+
throw new Error(`Plugin "${pluginName}"'s marker "@${name}" has a \`snippet\` that is not a non-empty path string.`);
|
|
1100
|
+
}
|
|
967
1101
|
if (rawDecl.deprecated !== undefined) {
|
|
968
1102
|
if (typeof rawDecl.deprecated !== "string" || !rawDecl.deprecated) {
|
|
969
1103
|
throw new Error(`Plugin "${pluginName}"'s marker "@${name}" has a \`deprecated\` value that is not a non-empty string.`);
|
|
@@ -1178,8 +1312,8 @@ function plugin(md, pluginOptions = {}) {
|
|
|
1178
1312
|
}
|
|
1179
1313
|
function openDeclaredMarker(meta, decl) {
|
|
1180
1314
|
const t = new state.Token("layout_component_open", decl.tag, 1);
|
|
1181
|
-
t.meta = { line: meta.__line };
|
|
1182
1315
|
const variant = meta.name || decl.presetVariant || null;
|
|
1316
|
+
t.meta = { line: meta.__line, component: meta.kind, variant, attrs: meta.attrs || {}, labelled: false };
|
|
1183
1317
|
const variantClass = variant && decl.variants && decl.variants[variant] || "";
|
|
1184
1318
|
const baseClass = [decl.classBase, variantClass].filter(Boolean).join(" ");
|
|
1185
1319
|
addClasses(t, baseClass, meta.attrs && meta.attrs.class ? meta.attrs.class : "");
|
|
@@ -1193,6 +1327,7 @@ function plugin(md, pluginOptions = {}) {
|
|
|
1193
1327
|
labelToken.content = `<${decl.label.tag} class="${escapeAttr(decl.label.class)}">${escapeHtml(value)}</${decl.label.tag}>
|
|
1194
1328
|
`;
|
|
1195
1329
|
out.push(labelToken);
|
|
1330
|
+
t.meta.labelled = true;
|
|
1196
1331
|
}
|
|
1197
1332
|
}
|
|
1198
1333
|
}
|
|
@@ -1454,6 +1589,25 @@ function plugin(md, pluginOptions = {}) {
|
|
|
1454
1589
|
stack.closeAll();
|
|
1455
1590
|
state.tokens = out;
|
|
1456
1591
|
});
|
|
1592
|
+
if (declaredMarkers && [...declaredMarkers.values()].some((d) => d.validate)) {
|
|
1593
|
+
md.core.ruler.push("gp_component_validate", function(state) {
|
|
1594
|
+
let lines = null;
|
|
1595
|
+
const tokens = state.tokens;
|
|
1596
|
+
for (let i = 0;i < tokens.length; i++) {
|
|
1597
|
+
const tok = tokens[i];
|
|
1598
|
+
if (tok.type !== "layout_component_open")
|
|
1599
|
+
continue;
|
|
1600
|
+
const decl = declaredMarkers.get(tok.meta.component);
|
|
1601
|
+
if (!decl || !decl.validate)
|
|
1602
|
+
continue;
|
|
1603
|
+
if (!lines)
|
|
1604
|
+
lines = state.src.split(`
|
|
1605
|
+
`);
|
|
1606
|
+
const from = i + (tok.meta.labelled ? 2 : 1);
|
|
1607
|
+
runComponentValidate(state.env, decl, tok.meta, tokens.slice(from, blockEnd(tokens, i)), lines);
|
|
1608
|
+
}
|
|
1609
|
+
});
|
|
1610
|
+
}
|
|
1457
1611
|
md.renderer.rules.layout_page_break = (tokens, idx) => {
|
|
1458
1612
|
const cls = tokens[idx].attrGet("class") || "gp-page-break";
|
|
1459
1613
|
const rangeAttr = tokens[idx].attrGet("data-source-range");
|
|
@@ -3860,7 +4014,7 @@ import git from "isomorphic-git";
|
|
|
3860
4014
|
// package.json
|
|
3861
4015
|
var package_default = {
|
|
3862
4016
|
name: "gutterpress",
|
|
3863
|
-
version: "0.11.
|
|
4017
|
+
version: "0.11.16-alpha.1",
|
|
3864
4018
|
description: "Markdown-to-PDF converter for professional print layout using a native Chromium print engine and Ghostscript.",
|
|
3865
4019
|
author: "itlackey",
|
|
3866
4020
|
license: "MPL-2.0",
|
|
@@ -6890,7 +7044,7 @@ var check24 = {
|
|
|
6890
7044
|
md.render(content, env);
|
|
6891
7045
|
for (const w of env.layoutWarnings ?? []) {
|
|
6892
7046
|
results.push(finding(check24.id, {
|
|
6893
|
-
severity: "warning",
|
|
7047
|
+
severity: w.severity ?? "warning",
|
|
6894
7048
|
message: w.message,
|
|
6895
7049
|
file,
|
|
6896
7050
|
...w.line > 0 ? { line: w.line } : {},
|
|
@@ -8475,7 +8629,7 @@ class PdfOutput {
|
|
|
8475
8629
|
const rawPdf = pdfxMode ? path8.join(stage, "raw.pdf") : path8.resolve(pdfFile);
|
|
8476
8630
|
await fsp2.mkdir(path8.dirname(path8.resolve(pdfFile)), { recursive: true });
|
|
8477
8631
|
log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
|
|
8478
|
-
const { buildNativePdf } = await import("./engine-
|
|
8632
|
+
const { buildNativePdf } = await import("./engine-szx34415.js");
|
|
8479
8633
|
const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
|
|
8480
8634
|
title: config.title,
|
|
8481
8635
|
author: config.authors.length > 0 ? config.authors.join(", ") : undefined,
|
|
@@ -11882,6 +12036,7 @@ async function readExtensionCss(projectDir, use) {
|
|
|
11882
12036
|
|
|
11883
12037
|
// src/lib/snippets.ts
|
|
11884
12038
|
var SNIPPETS_DIR = "snippets";
|
|
12039
|
+
var LEVEL_RANK = { project: 0, extension: 1, core: 2 };
|
|
11885
12040
|
var PLACEHOLDER_RE = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
|
|
11886
12041
|
function extractVariables(template) {
|
|
11887
12042
|
const seen = new Set;
|
|
@@ -11898,16 +12053,14 @@ function extractVariables(template) {
|
|
|
11898
12053
|
function substituteVariables(template, values) {
|
|
11899
12054
|
return template.replace(PLACEHOLDER_RE, (_full, name) => Object.prototype.hasOwnProperty.call(values, name) ? values[name] : "");
|
|
11900
12055
|
}
|
|
11901
|
-
function
|
|
12056
|
+
function resolveSnippetPath(projectDir, fileName) {
|
|
12057
|
+
const dir = path20.resolve(projectDir, SNIPPETS_DIR);
|
|
11902
12058
|
const full = path20.resolve(dir, fileName);
|
|
11903
12059
|
if (full !== path20.join(dir, path20.basename(fileName)) || path20.dirname(full) !== dir) {
|
|
11904
12060
|
throw new Error(`Unsafe snippet filename: ${fileName}`);
|
|
11905
12061
|
}
|
|
11906
12062
|
return full;
|
|
11907
12063
|
}
|
|
11908
|
-
function resolveSnippetPath(projectDir, fileName) {
|
|
11909
|
-
return resolveSafeChildFile(path20.resolve(projectDir, SNIPPETS_DIR), fileName);
|
|
11910
|
-
}
|
|
11911
12064
|
async function scanSnippetFiles(dir) {
|
|
11912
12065
|
let names;
|
|
11913
12066
|
try {
|
|
@@ -11928,7 +12081,8 @@ async function scanSnippetFiles(dir) {
|
|
|
11928
12081
|
entries.push({
|
|
11929
12082
|
name: prettify(fileName.replace(/\.md$/i, "")),
|
|
11930
12083
|
fileName,
|
|
11931
|
-
variables: extractVariables(body)
|
|
12084
|
+
variables: extractVariables(body),
|
|
12085
|
+
body
|
|
11932
12086
|
});
|
|
11933
12087
|
}
|
|
11934
12088
|
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -11938,9 +12092,6 @@ async function listSnippets(projectDir) {
|
|
|
11938
12092
|
const files = await scanSnippetFiles(path20.join(projectDir, SNIPPETS_DIR));
|
|
11939
12093
|
return files.map((file) => ({ ...file, source: { kind: "project" } }));
|
|
11940
12094
|
}
|
|
11941
|
-
async function readSnippet(projectDir, fileName) {
|
|
11942
|
-
return readFile30(resolveSnippetPath(projectDir, fileName), "utf8");
|
|
11943
|
-
}
|
|
11944
12095
|
async function saveSnippet(projectDir, name, body) {
|
|
11945
12096
|
const stem = slugify(name);
|
|
11946
12097
|
if (!stem)
|
|
@@ -11949,56 +12100,97 @@ async function saveSnippet(projectDir, name, body) {
|
|
|
11949
12100
|
const dir = path20.join(projectDir, SNIPPETS_DIR);
|
|
11950
12101
|
await mkdir11(dir, { recursive: true });
|
|
11951
12102
|
await writeFile10(resolveSnippetPath(projectDir, fileName), body, "utf8");
|
|
11952
|
-
return { name, fileName, variables: extractVariables(body), source: { kind: "project" } };
|
|
12103
|
+
return { name, fileName, variables: extractVariables(body), body, source: { kind: "project" } };
|
|
11953
12104
|
}
|
|
11954
12105
|
async function deleteSnippet(projectDir, fileName) {
|
|
11955
12106
|
await rm4(resolveSnippetPath(projectDir, fileName), { force: true });
|
|
11956
12107
|
}
|
|
11957
|
-
async function
|
|
11958
|
-
|
|
11959
|
-
for (const entry of await listProjectExtensions(projectDir)) {
|
|
11960
|
-
if (!entry.enabled || !entry.dir || !entry.carries.snippets)
|
|
11961
|
-
continue;
|
|
11962
|
-
const meta = await readExtensionMeta(entry.dir);
|
|
11963
|
-
const snippetsRel = meta.snippets?.trim();
|
|
11964
|
-
if (!snippetsRel)
|
|
11965
|
-
continue;
|
|
11966
|
-
out.push({ ref: entry.use, dir: entry.dir, name: entry.label, snippetsRel });
|
|
11967
|
-
}
|
|
11968
|
-
return out;
|
|
11969
|
-
}
|
|
11970
|
-
function extensionSnippetsDir(ext) {
|
|
11971
|
-
if (pathEscapesFolder(ext.snippetsRel))
|
|
12108
|
+
async function declaredSnippetsFolder(entry) {
|
|
12109
|
+
if (!entry.dir || !entry.carries.snippets)
|
|
11972
12110
|
return null;
|
|
11973
|
-
|
|
12111
|
+
const rel = (await readExtensionMeta(entry.dir)).snippets?.trim();
|
|
12112
|
+
return rel && !pathEscapesFolder(rel) ? rel : null;
|
|
11974
12113
|
}
|
|
11975
|
-
async function
|
|
11976
|
-
|
|
11977
|
-
|
|
11978
|
-
|
|
11979
|
-
|
|
11980
|
-
|
|
11981
|
-
|
|
11982
|
-
|
|
12114
|
+
async function declaredMarkers(entry, projectDir) {
|
|
12115
|
+
if (!entry.carries.markdown)
|
|
12116
|
+
return;
|
|
12117
|
+
try {
|
|
12118
|
+
return (await loadPlugin(extensionConfigFor(entry), projectDir)).markers;
|
|
12119
|
+
} catch {
|
|
12120
|
+
return;
|
|
12121
|
+
}
|
|
12122
|
+
}
|
|
12123
|
+
async function collectLibrary(projectDir) {
|
|
12124
|
+
const groups = [];
|
|
12125
|
+
const components = [];
|
|
12126
|
+
for (const entry of await listProjectExtensions(projectDir)) {
|
|
12127
|
+
if (!entry.enabled)
|
|
11983
12128
|
continue;
|
|
11984
|
-
|
|
11985
|
-
|
|
12129
|
+
const source = { kind: "extension", ref: entry.use, name: entry.label };
|
|
12130
|
+
const folder = await declaredSnippetsFolder(entry);
|
|
12131
|
+
const snippets2 = folder ? (await scanSnippetFiles(path20.join(entry.dir, folder))).map((file) => ({ ...file, source })) : [];
|
|
12132
|
+
for (const [name, decl] of Object.entries(await declaredMarkers(entry, projectDir) ?? {})) {
|
|
12133
|
+
if (decl.deprecated !== undefined)
|
|
12134
|
+
continue;
|
|
12135
|
+
components.push({ name, source });
|
|
12136
|
+
if (!entry.dir)
|
|
12137
|
+
continue;
|
|
12138
|
+
const own = typeof decl.snippet === "string" && decl.snippet.trim() ? decl.snippet.trim() : null;
|
|
12139
|
+
const rel = own ?? path20.join(folder ?? SNIPPETS_DIR, `${name}.md`);
|
|
12140
|
+
if (pathEscapesFolder(rel))
|
|
12141
|
+
continue;
|
|
12142
|
+
const listed = folder && path20.dirname(path20.join(entry.dir, rel)) === path20.join(entry.dir, folder) ? snippets2.find((s) => s.fileName === path20.basename(rel)) : undefined;
|
|
12143
|
+
if (listed) {
|
|
12144
|
+
listed.component = name;
|
|
12145
|
+
continue;
|
|
12146
|
+
}
|
|
12147
|
+
const body = await readFile30(path20.join(entry.dir, rel), "utf8").catch(() => null);
|
|
12148
|
+
if (body === null)
|
|
11986
12149
|
continue;
|
|
11987
|
-
|
|
12150
|
+
snippets2.push({
|
|
12151
|
+
name: prettify(path20.basename(rel).replace(/\.md$/i, "")),
|
|
12152
|
+
fileName: rel.split(path20.sep).join("/"),
|
|
12153
|
+
variables: extractVariables(body),
|
|
12154
|
+
body,
|
|
12155
|
+
source,
|
|
12156
|
+
component: name
|
|
12157
|
+
});
|
|
12158
|
+
}
|
|
12159
|
+
if (snippets2.length) {
|
|
12160
|
+
snippets2.sort((a, b) => a.name.localeCompare(b.name));
|
|
12161
|
+
groups.push({ label: entry.label, snippets: snippets2 });
|
|
11988
12162
|
}
|
|
11989
12163
|
}
|
|
11990
|
-
|
|
11991
|
-
|
|
11992
|
-
|
|
11993
|
-
|
|
11994
|
-
|
|
11995
|
-
|
|
12164
|
+
const book = await listSnippets(projectDir);
|
|
12165
|
+
const names = new Set(components.map((c) => c.name));
|
|
12166
|
+
for (const s of book) {
|
|
12167
|
+
const stem = s.fileName.replace(/\.md$/i, "").toLowerCase();
|
|
12168
|
+
if (names.has(stem))
|
|
12169
|
+
s.component = stem;
|
|
11996
12170
|
}
|
|
11997
|
-
|
|
11998
|
-
|
|
11999
|
-
|
|
12171
|
+
groups.sort((a, b) => a.label.localeCompare(b.label));
|
|
12172
|
+
const snippets = [...book, ...groups.flatMap((g) => g.snippets)];
|
|
12173
|
+
for (const component of components) {
|
|
12174
|
+
let best;
|
|
12175
|
+
for (const s of snippets) {
|
|
12176
|
+
if (s.component !== component.name)
|
|
12177
|
+
continue;
|
|
12178
|
+
if (s.source.kind === "extension" && s.source.ref !== component.source.ref)
|
|
12179
|
+
continue;
|
|
12180
|
+
if (!best || LEVEL_RANK[s.source.kind] < LEVEL_RANK[best.source.kind])
|
|
12181
|
+
best = s;
|
|
12182
|
+
}
|
|
12183
|
+
if (best)
|
|
12184
|
+
component.snippet = best.body;
|
|
12000
12185
|
}
|
|
12001
|
-
|
|
12186
|
+
components.sort((a, b) => a.name.localeCompare(b.name));
|
|
12187
|
+
return { snippets, components };
|
|
12188
|
+
}
|
|
12189
|
+
async function listMergedSnippets(projectDir) {
|
|
12190
|
+
return (await collectLibrary(projectDir)).snippets;
|
|
12191
|
+
}
|
|
12192
|
+
async function listMarkerComponents(projectDir) {
|
|
12193
|
+
return (await collectLibrary(projectDir)).components;
|
|
12002
12194
|
}
|
|
12003
12195
|
// src/lib/extension-import.ts
|
|
12004
12196
|
import { existsSync as existsSync15 } from "node:fs";
|
|
@@ -16036,4 +16228,4 @@ async function setPublishProviderConfig(projectDir, providerId, values) {
|
|
|
16036
16228
|
await writeManifestDoc(file, doc);
|
|
16037
16229
|
return settingsFromDoc(doc);
|
|
16038
16230
|
}
|
|
16039
|
-
export { BUNDLED_EXTENSIONS, isBundledExtension, isPathSpecifier, parseExtensionSpecifier, PRESET_IDS, PRESETS, TARGETS, TARGET_IDS, publishTargetFor, MANIFEST_FILENAMES, hasProjectManifest, loadManifest, loadManifestWithPath, resolveConfig, SYNC_SNAPSHOT_MESSAGE, log, resolveActiveStyles, listProjectStyles, EXTENSION_MANIFEST_FILENAME, readExtensionMeta, extensionStyleList, extensionEntry, extensionCarries, pathEscapesFolder, assertExtensionContained, resolveExtension, getChecks, getCheckById, resolveCheckSelectors, formatReport, ruleRemoteUrls, ruleRiskyProps, ruleSyntax, checkCss, inspectImage, runChecks, checkToolAvailability, reportMissingTools, executeValidation, executeAndReport, splitOutPath, runBuild, openPath, startPreviewServer, defaultConfigDir, FileTokenStore, extractUrlCredential, getSystemDiagnostics, slugifyProjectName, escapeYamlScalar, scaffoldProject, adoptFolder, ensureGitignoreHasDist, EXTENSION_KINDS, RESERVED_PREFIX, resolveExtensionPrefix, scaffoldExtension, BUILT_IN_TEMPLATE_IDS, listBuiltInTemplates, saveProjectAsTemplate, listCustomTemplates, importTemplateFromFolder, DEFAULT_NPM_REGISTRY, npmRegistryUrl, EXTENSIONS_DIR, RECOMMENDED_EXTENSIONS, BUILT_IN_STYLE_SET_IDS, sameExtension, listProjectExtensions, describeExtension, setExtensionEnabled, reorderExtensions, removeExtension, addExtension, checkExtensionUpdates, updateExtensions, listBuiltInStyleSets, addBuiltInStyleSet, validateProjectExtensions, readExtensionCss, SNIPPETS_DIR, extractVariables, substituteVariables, listSnippets,
|
|
16231
|
+
export { BUNDLED_EXTENSIONS, isBundledExtension, isPathSpecifier, parseExtensionSpecifier, PRESET_IDS, PRESETS, TARGETS, TARGET_IDS, publishTargetFor, MANIFEST_FILENAMES, hasProjectManifest, loadManifest, loadManifestWithPath, resolveConfig, SYNC_SNAPSHOT_MESSAGE, log, resolveActiveStyles, listProjectStyles, EXTENSION_MANIFEST_FILENAME, readExtensionMeta, extensionStyleList, extensionEntry, extensionCarries, pathEscapesFolder, assertExtensionContained, resolveExtension, getChecks, getCheckById, resolveCheckSelectors, formatReport, ruleRemoteUrls, ruleRiskyProps, ruleSyntax, checkCss, inspectImage, runChecks, checkToolAvailability, reportMissingTools, executeValidation, executeAndReport, splitOutPath, runBuild, openPath, startPreviewServer, defaultConfigDir, FileTokenStore, extractUrlCredential, getSystemDiagnostics, slugifyProjectName, escapeYamlScalar, scaffoldProject, adoptFolder, ensureGitignoreHasDist, EXTENSION_KINDS, RESERVED_PREFIX, resolveExtensionPrefix, scaffoldExtension, BUILT_IN_TEMPLATE_IDS, listBuiltInTemplates, saveProjectAsTemplate, listCustomTemplates, importTemplateFromFolder, DEFAULT_NPM_REGISTRY, npmRegistryUrl, EXTENSIONS_DIR, RECOMMENDED_EXTENSIONS, BUILT_IN_STYLE_SET_IDS, sameExtension, listProjectExtensions, describeExtension, setExtensionEnabled, reorderExtensions, removeExtension, addExtension, checkExtensionUpdates, updateExtensions, listBuiltInStyleSets, addBuiltInStyleSet, validateProjectExtensions, readExtensionCss, SNIPPETS_DIR, extractVariables, substituteVariables, listSnippets, saveSnippet, deleteSnippet, listMergedSnippets, listMarkerComponents, importExtensionFromFile, importExtensionFromUrl, MAX_SEARCH_BYTES, SEARCH_KEYWORDS, searchNpmExtensions, setManifestFields, readManifestFields, setActiveStyles, AUTO_SNAPSHOT_DEFAULT_MINUTES, AUTO_SYNC_MIN_MINUTES, AUTO_SYNC_MAX_MINUTES, AUTO_SYNC_DEFAULT_MINUTES, AUTO_SYNC_PUSH_INTERVAL_MINUTES, autoSnapshotDelayMs, autoSyncDelayMs, isGitInternalPath, resolveGitHubClientId, GITHUB_HOST, GitHubAuthProvider, GDRIVE_HOST, resolveGoogleClientId, resolveGoogleClientSecret, GOOGLE_NOT_CONFIGURED_MESSAGE, requireGoogleClientCredentials, pkceChallengeFromVerifier, GoogleAuthProvider, revokeGoogleCredential, publishCredentialKey, publishConnectionStatus, listPublishAccounts, connectGoogleDrive, listGitHubRepositories, listRepoBooks, listGitHubBranches, sanitizeCloneFolderName, cloneRepository, isSshRemoteUrl, testRemoteAccess, knownForgeTokenUrl, connectGenericHost, parseRemoteOrigin, forgeKindForHost, diagnoseProjectRemote, syncProject, refreshRemoteCopies, REPAIR_SNAPSHOT_MESSAGE, repairOnlineBackup, scorchedEarth, listPublishProviders, publishProviderFor, resolvePublishFormat, resolvePublishRequest, runPublish, connectPublishProvider, disconnectPublishCredential, PublishSelectionsStore, readPublishSettings, setPublishProviderConfig };
|
|
@@ -68,10 +68,10 @@ var chapter_01_default3 = "./chapter-01-6fn9pvd0.md";
|
|
|
68
68
|
var package_default = "./package-9rv69pxm.json";
|
|
69
69
|
|
|
70
70
|
// src/assets/extension-templates/plugin/README.md
|
|
71
|
-
var README_default = "./README-
|
|
71
|
+
var README_default = "./README-vsnsc1b3.md";
|
|
72
72
|
|
|
73
73
|
// src/assets/extension-templates/plugin/plugin.js.tpl
|
|
74
|
-
var plugin_js_default = "./plugin.js-
|
|
74
|
+
var plugin_js_default = "./plugin.js-z492w01r.tpl";
|
|
75
75
|
|
|
76
76
|
// src/assets/extension-templates/plugin/styles/plugin.css
|
|
77
77
|
var plugin_default = "./plugin-spbcem4j.css";
|
|
@@ -86,7 +86,7 @@ var fixture_default = "./fixture-2qnyqgb5.md";
|
|
|
86
86
|
var expected_default = "./expected-ycmacem8.html";
|
|
87
87
|
|
|
88
88
|
// src/assets/extension-templates/plugin/test/plugin.test.js.tpl
|
|
89
|
-
var plugin_test_js_default = "./plugin.test.js-
|
|
89
|
+
var plugin_test_js_default = "./plugin.test.js-6n8ct5e3.tpl";
|
|
90
90
|
|
|
91
91
|
// src/assets/extension-templates/theme/package.json
|
|
92
92
|
var package_default2 = "./package-aenwjew3.json";
|
package/dist/index.d.ts
CHANGED
|
@@ -30,4 +30,4 @@ export { BUILD_DIAGNOSTIC_CODES } from "./engine/compiler/build.ts";
|
|
|
30
30
|
export type { BuildDiagnostic, BuildDiagnosticCode } from "./engine/compiler/build.ts";
|
|
31
31
|
export type { PrintSafeWarning } from "./lib/printsafe.ts";
|
|
32
32
|
export { ruleRemoteUrls, ruleRiskyProps, ruleSyntax, } from "./lib/printsafe.ts";
|
|
33
|
-
export type { GutterpressPlugin, GutterpressPluginExport, GutterpressPluginMetadata, } from "./lib/markdown/plugins.ts";
|
|
33
|
+
export type { GutterpressComponent, GutterpressComponentBlock, GutterpressComponentProblem, GutterpressPlugin, GutterpressPluginExport, GutterpressPluginMetadata, } from "./lib/markdown/plugins.ts";
|
package/dist/index.js
CHANGED
|
@@ -77,6 +77,7 @@ import {
|
|
|
77
77
|
listCustomTemplates,
|
|
78
78
|
listGitHubBranches,
|
|
79
79
|
listGitHubRepositories,
|
|
80
|
+
listMarkerComponents,
|
|
80
81
|
listMergedSnippets,
|
|
81
82
|
listProjectExtensions,
|
|
82
83
|
listProjectStyles,
|
|
@@ -99,10 +100,8 @@ import {
|
|
|
99
100
|
publishTargetFor,
|
|
100
101
|
readExtensionCss,
|
|
101
102
|
readExtensionMeta,
|
|
102
|
-
readExtensionSnippet,
|
|
103
103
|
readManifestFields,
|
|
104
104
|
readPublishSettings,
|
|
105
|
-
readSnippet,
|
|
106
105
|
refreshRemoteCopies,
|
|
107
106
|
removeExtension,
|
|
108
107
|
reorderExtensions,
|
|
@@ -146,13 +145,13 @@ import {
|
|
|
146
145
|
testRemoteAccess,
|
|
147
146
|
updateExtensions,
|
|
148
147
|
validateProjectExtensions
|
|
149
|
-
} from "./index-
|
|
148
|
+
} from "./index-azdfgy3t.js";
|
|
150
149
|
import {
|
|
151
150
|
BUILD_DIAGNOSTIC_CODES
|
|
152
|
-
} from "./index-
|
|
151
|
+
} from "./index-9ydc9q9q.js";
|
|
153
152
|
import {
|
|
154
153
|
BuildError
|
|
155
|
-
} from "./index-
|
|
154
|
+
} from "./index-eawjxgkn.js";
|
|
156
155
|
import {
|
|
157
156
|
DEFAULT_PRINT_OPTS,
|
|
158
157
|
assertMilestone,
|
|
@@ -232,10 +231,8 @@ export {
|
|
|
232
231
|
removeExtension,
|
|
233
232
|
refreshRemoteCopies,
|
|
234
233
|
readyProbeExpr,
|
|
235
|
-
readSnippet,
|
|
236
234
|
readPublishSettings,
|
|
237
235
|
readManifestFields,
|
|
238
|
-
readExtensionSnippet,
|
|
239
236
|
readExtensionMeta,
|
|
240
237
|
readExtensionCss,
|
|
241
238
|
publishTargetFor,
|
|
@@ -259,6 +256,7 @@ export {
|
|
|
259
256
|
listProjectStyles,
|
|
260
257
|
listProjectExtensions,
|
|
261
258
|
listMergedSnippets,
|
|
259
|
+
listMarkerComponents,
|
|
262
260
|
listLocalBranches,
|
|
263
261
|
listGitHubRepositories,
|
|
264
262
|
listGitHubBranches,
|
|
@@ -24,6 +24,11 @@ export type ReadText = (relPath: string) => Promise<string>;
|
|
|
24
24
|
* same type, via a second, unconditional check modeled on `unknown_gp_class`
|
|
25
25
|
* rather than the core-only `scanForMistypedMarkers`.
|
|
26
26
|
*
|
|
27
|
+
* A declared marker's opt-in `validate` adds two more: `component_invalid`
|
|
28
|
+
* (one problem the validator reported — the only warning that may carry a
|
|
29
|
+
* `severity`) and `component_validate_failed` (the validator threw or
|
|
30
|
+
* returned something unusable).
|
|
31
|
+
*
|
|
27
32
|
* There is deliberately no `section_without_page`: a @section with no open
|
|
28
33
|
* @page is valid authoring (17/17 false positives across two real books).
|
|
29
34
|
*/
|
|
@@ -32,6 +37,8 @@ export interface LayoutWarning {
|
|
|
32
37
|
type: string;
|
|
33
38
|
message: string;
|
|
34
39
|
marker?: unknown;
|
|
40
|
+
/** Set only by a component's `validate`; absent means `"warning"`. */
|
|
41
|
+
severity?: "error" | "warning" | "info";
|
|
35
42
|
}
|
|
36
43
|
/**
|
|
37
44
|
* The Chromium milestone that paints a `@page { background: url() }` image
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ResolvedExtensionConfig } from "../../schema/manifest.types";
|
|
2
2
|
import type { LoadedPlugin } from "./renderer";
|
|
3
|
-
export type { GutterpressMarkerDeclaration, GutterpressMarkerLabel, GutterpressMarkerTable, GutterpressPlugin, GutterpressPluginMetadata, GutterpressPluginExport, LoadedPlugin, } from "./renderer";
|
|
3
|
+
export type { GutterpressComponent, GutterpressComponentBlock, GutterpressComponentProblem, GutterpressMarkerDeclaration, GutterpressMarkerLabel, GutterpressMarkerTable, GutterpressPlugin, GutterpressPluginMetadata, GutterpressPluginExport, LoadedPlugin, } from "./renderer";
|
|
4
4
|
export { applyPlugins, collectPluginCss, collectPluginStylePaths, collectPluginStyleGroups } from "./renderer";
|
|
5
5
|
export type { PluginStyleGroup } from "./renderer";
|
|
6
6
|
import { type PluginStyleGroup } from "./renderer";
|
|
@@ -106,7 +106,98 @@ export interface GutterpressMarkerDeclaration {
|
|
|
106
106
|
};
|
|
107
107
|
/** This marker is retired: using it (or its `@end-` form) warns with this message and is otherwise a no-op. */
|
|
108
108
|
deprecated?: string;
|
|
109
|
+
/**
|
|
110
|
+
* The example content the desktop editor inserts for this component, as a
|
|
111
|
+
* path relative to the extension's own folder (where its package.json
|
|
112
|
+
* lives). Defaults to `<snippets folder>/<marker name>.md`. Read by the
|
|
113
|
+
* host's component listing only — it never affects rendering.
|
|
114
|
+
*/
|
|
115
|
+
snippet?: string;
|
|
116
|
+
/**
|
|
117
|
+
* Opt-in structure check, run every time the component is rendered
|
|
118
|
+
* (validate, Problems panel, build, preview). Receives a plain description
|
|
119
|
+
* of the component's content — see {@link GutterpressComponent} — and
|
|
120
|
+
* returns its problems. Must be synchronous; ignored on an alias (which
|
|
121
|
+
* uses its target's) and on a deprecated entry.
|
|
122
|
+
*/
|
|
123
|
+
validate?: (component: GutterpressComponent) => GutterpressComponentProblems;
|
|
124
|
+
}
|
|
125
|
+
/** One top-level block inside a component, as its `validate` sees it. `line` is 1-based in the file. */
|
|
126
|
+
export type GutterpressComponentBlock = {
|
|
127
|
+
type: "heading";
|
|
128
|
+
level: number;
|
|
129
|
+
text: string;
|
|
130
|
+
line: number;
|
|
131
|
+
} | {
|
|
132
|
+
type: "paragraph";
|
|
133
|
+
text: string;
|
|
134
|
+
line: number;
|
|
135
|
+
} | {
|
|
136
|
+
type: "image";
|
|
137
|
+
alt: string;
|
|
138
|
+
src: string;
|
|
139
|
+
line: number;
|
|
140
|
+
} | {
|
|
141
|
+
type: "list";
|
|
142
|
+
ordered: boolean;
|
|
143
|
+
items: string[];
|
|
144
|
+
line: number;
|
|
145
|
+
} | {
|
|
146
|
+
type: "quote";
|
|
147
|
+
text: string;
|
|
148
|
+
line: number;
|
|
149
|
+
} | {
|
|
150
|
+
type: "code";
|
|
151
|
+
lang: string;
|
|
152
|
+
text: string;
|
|
153
|
+
line: number;
|
|
154
|
+
} | {
|
|
155
|
+
type: "table" | "rule";
|
|
156
|
+
line: number;
|
|
157
|
+
} | {
|
|
158
|
+
type: "html";
|
|
159
|
+
text: string;
|
|
160
|
+
line: number;
|
|
161
|
+
} | {
|
|
162
|
+
type: "component";
|
|
163
|
+
name: string;
|
|
164
|
+
line: number;
|
|
109
165
|
}
|
|
166
|
+
/** Any other block (e.g. one another plugin adds), named after its markdown-it block type. */
|
|
167
|
+
| {
|
|
168
|
+
type: string;
|
|
169
|
+
line: number;
|
|
170
|
+
};
|
|
171
|
+
/**
|
|
172
|
+
* What a declared marker's `validate` receives: a fresh, plain copy of one
|
|
173
|
+
* use of the component (changing it has no effect). `text` fields are raw
|
|
174
|
+
* markdown — inline formatting is not parsed.
|
|
175
|
+
*/
|
|
176
|
+
export interface GutterpressComponent {
|
|
177
|
+
/** The marker as the author typed it, e.g. `"term-box"`. */
|
|
178
|
+
name: string;
|
|
179
|
+
/** The bare word after the marker (`@term-box note` → `"note"`), or null. */
|
|
180
|
+
variant: string | null;
|
|
181
|
+
/** The marker's attributes, e.g. `{ label: "Read this first" }`. */
|
|
182
|
+
attrs: Record<string, string>;
|
|
183
|
+
/** The marker's own 1-based line. */
|
|
184
|
+
line: number;
|
|
185
|
+
/** The raw markdown inside the component, from the line after the marker through its last line of content. */
|
|
186
|
+
text: string;
|
|
187
|
+
/** The component's top-level content, in order. */
|
|
188
|
+
blocks: GutterpressComponentBlock[];
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* One problem from `validate`: a message, or a message with a 1-based `line`
|
|
192
|
+
* (default: the marker's line) and `severity` (default `"warning"`).
|
|
193
|
+
*/
|
|
194
|
+
export type GutterpressComponentProblem = string | {
|
|
195
|
+
message: string;
|
|
196
|
+
line?: number;
|
|
197
|
+
severity?: "error" | "warning" | "info";
|
|
198
|
+
};
|
|
199
|
+
/** `validate`'s return value: nothing, one message, or a list of problems. */
|
|
200
|
+
export type GutterpressComponentProblems = GutterpressComponentProblem[] | string | void | null | undefined;
|
|
110
201
|
/** A plugin's full declared marker table — see {@link GutterpressMarkerDeclaration}. */
|
|
111
202
|
export type GutterpressMarkerTable = Record<string, GutterpressMarkerDeclaration>;
|
|
112
203
|
/**
|