@wizzlethorpe/vaults 0.13.2 → 0.14.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/dist/asset-refs.js +274 -0
- package/dist/asset-refs.js.map +1 -0
- package/dist/auth.js.map +1 -1
- package/dist/build.js +252 -492
- package/dist/build.js.map +1 -1
- package/dist/commands/build.js +54 -5
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/preview.js +0 -4
- package/dist/commands/preview.js.map +1 -1
- package/dist/commands/push.js +4 -9
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/role.js +55 -14
- package/dist/commands/role.js.map +1 -1
- package/dist/config.js +25 -5
- package/dist/config.js.map +1 -1
- package/dist/foundry-importer.bundle.js +1228 -327
- package/dist/foundry-importer.js +2 -7
- package/dist/foundry-importer.js.map +1 -1
- package/dist/foundry-meta.js +284 -0
- package/dist/foundry-meta.js.map +1 -0
- package/dist/foundry-module-journal.js +112 -0
- package/dist/foundry-module-journal.js.map +1 -0
- package/dist/foundry-module-render.js +75 -0
- package/dist/foundry-module-render.js.map +1 -0
- package/dist/foundry-module.js +1075 -0
- package/dist/foundry-module.js.map +1 -0
- package/dist/frontmatter-defaults.js +68 -0
- package/dist/frontmatter-defaults.js.map +1 -0
- package/dist/index.js +9 -9
- package/dist/index.js.map +1 -1
- package/dist/manifest.js +115 -0
- package/dist/manifest.js.map +1 -0
- package/dist/render/auth-template.js +323 -35
- package/dist/render/auth-template.js.map +1 -1
- package/dist/render/bases.js +22 -38
- package/dist/render/bases.js.map +1 -1
- package/dist/render/cover.js +23 -1
- package/dist/render/cover.js.map +1 -1
- package/dist/render/handlers/builtin/download.js +90 -0
- package/dist/render/handlers/builtin/download.js.map +1 -0
- package/dist/render/handlers/builtin/foundry-manifest.js +158 -0
- package/dist/render/handlers/builtin/foundry-manifest.js.map +1 -0
- package/dist/render/handlers/builtin/index.js +3 -1
- package/dist/render/handlers/builtin/index.js.map +1 -1
- package/dist/render/pipeline.js +4 -1
- package/dist/render/pipeline.js.map +1 -1
- package/dist/render/slug.js +0 -5
- package/dist/render/slug.js.map +1 -1
- package/dist/scan.js +8 -0
- package/dist/scan.js.map +1 -1
- package/dist/settings.js +112 -6
- package/dist/settings.js.map +1 -1
- package/package.json +2 -1
package/dist/foundry-importer.js
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
// Build helper: the Foundry-side importer is pre-bundled at CLI build time
|
|
2
2
|
// (see scripts/bundle-importer.mjs) into `dist/foundry-importer.bundle.js`,
|
|
3
3
|
// which ships inside the npm package. At deploy time we copy that bundle to
|
|
4
|
-
// `_foundry/importer.js
|
|
5
|
-
// `{ version, sha256 }` so the Foundry host can detect skew before evaluating.
|
|
4
|
+
// `_foundry/importer.js`, which the Foundry host fetches and evaluates.
|
|
6
5
|
//
|
|
7
6
|
// Bundling ahead of publish (not here) means the installed CLI never runs
|
|
8
7
|
// esbuild on the user's machine and doesn't need the foundry/ source tree,
|
|
9
8
|
// which isn't part of the npm package.
|
|
10
|
-
import { createHash } from "node:crypto";
|
|
11
9
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
12
10
|
import { basename, dirname, join } from "node:path";
|
|
13
11
|
import { fileURLToPath } from "node:url";
|
|
14
|
-
import { CLI_VERSION } from "./version.js";
|
|
15
12
|
// scripts/bundle-importer.mjs always emits the bundle into dist/. This module
|
|
16
13
|
// runs from dist/ once published, but from src/ under tsx (tests) — resolve to
|
|
17
14
|
// dist/ either way.
|
|
@@ -19,17 +16,15 @@ const here = dirname(fileURLToPath(import.meta.url));
|
|
|
19
16
|
const distDir = basename(here) === "src" ? join(here, "..", "dist") : here;
|
|
20
17
|
const BUNDLE_PATH = join(distDir, "foundry-importer.bundle.js");
|
|
21
18
|
/**
|
|
22
|
-
* Write `_foundry/importer.js`
|
|
19
|
+
* Write `_foundry/importer.js` into the deploy.
|
|
23
20
|
* Called from build.ts after the variant outputs are in place — the
|
|
24
21
|
* bundle is a shared root-level asset (not per-variant), since it has
|
|
25
22
|
* no role-gated content.
|
|
26
23
|
*/
|
|
27
24
|
export async function writeFoundryImporter(outputDir) {
|
|
28
25
|
const source = await readFile(BUNDLE_PATH, "utf8");
|
|
29
|
-
const sha256 = createHash("sha256").update(source).digest("hex");
|
|
30
26
|
const dir = join(outputDir, "_foundry");
|
|
31
27
|
await mkdir(dir, { recursive: true });
|
|
32
28
|
await writeFile(join(dir, "importer.js"), source);
|
|
33
|
-
await writeFile(join(dir, "version.json"), JSON.stringify({ version: CLI_VERSION, sha256 }, null, 2));
|
|
34
29
|
}
|
|
35
30
|
//# sourceMappingURL=foundry-importer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"foundry-importer.js","sourceRoot":"","sources":["../src/foundry-importer.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,4EAA4E;AAC5E,4EAA4E;AAC5E,
|
|
1
|
+
{"version":3,"file":"foundry-importer.js","sourceRoot":"","sources":["../src/foundry-importer.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,4EAA4E;AAC5E,4EAA4E;AAC5E,wEAAwE;AACxE,EAAE;AACF,0EAA0E;AAC1E,2EAA2E;AAC3E,uCAAuC;AAEvC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,8EAA8E;AAC9E,+EAA+E;AAC/E,oBAAoB;AACpB,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACrD,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC3E,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,4BAA4B,CAAC,CAAC;AAEhE;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,SAAiB;IAC1D,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IACnD,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IACxC,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,EAAE,MAAM,CAAC,CAAC;AACpD,CAAC"}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
// Everything the CLI knows about the `foundry:` frontmatter block.
|
|
2
|
+
//
|
|
3
|
+
// Kept in one file because this is a shared contract, not a private detail:
|
|
4
|
+
// foundry/scripts/ reads exactly these keys back out of the manifest, and the
|
|
5
|
+
// two sides disagreeing is how `base: actor:npc` once created an Actor while
|
|
6
|
+
// every inbound wikilink addressed a nonexistent "actor". The doc-type rule
|
|
7
|
+
// here is held to foundry/scripts/foundry-base.mjs by
|
|
8
|
+
// cli/test/foundry-base-conformance.test.ts.
|
|
9
|
+
import { readFile } from "node:fs/promises";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
/**
|
|
12
|
+
* Whether a `foundry.base` will actually produce a document.
|
|
13
|
+
*
|
|
14
|
+
* Both forms do, for every type vaults can instantiate. This used to be
|
|
15
|
+
* narrower — the module cloned from a UUID only for Actor and Item, so a lone
|
|
16
|
+
* `Compendium.<pkg>.<pack>.Scene.<id>` created nothing and warning about a
|
|
17
|
+
* collision would have described documents that never exist. That restriction
|
|
18
|
+
* is gone (map packs ship their content as compendium Scenes, and those were
|
|
19
|
+
* all being skipped), so a well-formed base always produces a document and
|
|
20
|
+
* the only question left is whether the type is one we recognise.
|
|
21
|
+
*
|
|
22
|
+
* All entries name the same type by the time this runs, so `docType` answers
|
|
23
|
+
* for the whole list.
|
|
24
|
+
*/
|
|
25
|
+
function willInstantiate(_specs, docType) {
|
|
26
|
+
return canonicalFoundryType(docType) !== null;
|
|
27
|
+
}
|
|
28
|
+
export function warnFoundryDocCollisions(pages) {
|
|
29
|
+
const seen = new Map(); // key → first page path
|
|
30
|
+
for (const p of pages) {
|
|
31
|
+
const fo = p.frontmatter?.["foundry"];
|
|
32
|
+
if (!fo || typeof fo !== "object" || Array.isArray(fo))
|
|
33
|
+
continue;
|
|
34
|
+
const base = fo["base"];
|
|
35
|
+
const specs = (Array.isArray(base) ? base : [base])
|
|
36
|
+
.filter((x) => typeof x === "string" && x.length > 0);
|
|
37
|
+
if (specs.length === 0)
|
|
38
|
+
continue;
|
|
39
|
+
const docType = foundryBaseDocName(specs[0]);
|
|
40
|
+
if (!docType)
|
|
41
|
+
continue;
|
|
42
|
+
if (!willInstantiate(specs, docType))
|
|
43
|
+
continue;
|
|
44
|
+
const override = fo["folder"];
|
|
45
|
+
const folder = typeof override === "string" && override.trim()
|
|
46
|
+
? override.trim().replace(/^\/+|\/+$/g, "")
|
|
47
|
+
: p.path.split("/").slice(0, -1).join("/");
|
|
48
|
+
const name = p.title || p.path.split("/").pop().replace(/\.md$/i, "");
|
|
49
|
+
const key = `${docType}\u0000${folder}\u0000${name}`;
|
|
50
|
+
const previous = seen.get(key);
|
|
51
|
+
if (previous === undefined) {
|
|
52
|
+
seen.set(key, p.path);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
console.warn(` ${p.path}: would create a ${docType} named '${name}' in the same Foundry `
|
|
56
|
+
+ `folder ('${folder || "(vault root)"}') as '${previous}'. Rename one, or `
|
|
57
|
+
+ `separate them with foundry.folder.`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The document type a `foundry.base` spec names, read off the string without
|
|
62
|
+
* resolving anything. Every UUID form puts the type second-to-last
|
|
63
|
+
* (`Actor.<id>`, `Compendium.<pkg>.<pack>.Actor.<id>`), and a blank-doc spec
|
|
64
|
+
* (`Actor:npc`) carries it outright. `links.mjs` derives it the same way, so
|
|
65
|
+
* both sides agree on where a wikilink to the page points.
|
|
66
|
+
*/
|
|
67
|
+
export function foundryBaseDocName(spec) {
|
|
68
|
+
// A Moulinette reference names no type. Checked before the UUID rule
|
|
69
|
+
// because its file segment ends in ".json", which the dot test would
|
|
70
|
+
// otherwise read as a UUID. The type comes from another entry in the list;
|
|
71
|
+
// normalizeFoundryBase makes sure one is there.
|
|
72
|
+
if (spec.startsWith("@moulinette/"))
|
|
73
|
+
return null;
|
|
74
|
+
if (spec.includes(".")) {
|
|
75
|
+
const parts = spec.split(".");
|
|
76
|
+
if (parts.length < 2)
|
|
77
|
+
return null;
|
|
78
|
+
const raw = parts[parts.length - 2];
|
|
79
|
+
// Unknown types pass through: vaults can't instantiate a Combat, but
|
|
80
|
+
// Foundry may still resolve the UUID, and reporting the type beats
|
|
81
|
+
// claiming the spec names none.
|
|
82
|
+
return canonicalFoundryType(raw) ?? raw ?? null;
|
|
83
|
+
}
|
|
84
|
+
// Blank-document form. An unrecognised type is not a base at all, so it is
|
|
85
|
+
// rejected rather than passed through — matching the Foundry module, which
|
|
86
|
+
// would create nothing for it.
|
|
87
|
+
return canonicalFoundryType(spec.split(":")[0]);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Fold a type segment to its canonical spelling, or null if vaults doesn't
|
|
91
|
+
* instantiate it.
|
|
92
|
+
*
|
|
93
|
+
* Case matters downstream and is hand-typed here: `base: actor:npc` is
|
|
94
|
+
* supported, but Foundry's `@UUID[...]` enricher does a case-sensitive
|
|
95
|
+
* lookup. Returning "actor" made the CLI treat it as a different type from
|
|
96
|
+
* "Actor" — so a list mixing the two failed the same-type check and had its
|
|
97
|
+
* whole foundry.base dropped. Kept in step with
|
|
98
|
+
* foundry/scripts/foundry-base.mjs by cli/test/foundry-base-conformance.test.ts.
|
|
99
|
+
*/
|
|
100
|
+
export function canonicalFoundryType(raw) {
|
|
101
|
+
if (!raw)
|
|
102
|
+
return null;
|
|
103
|
+
return FOUNDRY_BLANK_DOC_TYPES.find((t) => t.toLowerCase() === raw.toLowerCase()) ?? null;
|
|
104
|
+
}
|
|
105
|
+
export const FOUNDRY_BLANK_DOC_TYPES = [
|
|
106
|
+
"Actor", "Item", "Scene", "JournalEntry",
|
|
107
|
+
"RollTable", "Macro", "Cards", "Playlist",
|
|
108
|
+
];
|
|
109
|
+
/**
|
|
110
|
+
* Validate `foundry.base` and normalize it for the manifest: a single string
|
|
111
|
+
* stays a string (so an older Foundry module keeps working), a list of two or
|
|
112
|
+
* more stays a list. Returns null when the value can't be used, having said
|
|
113
|
+
* why — the build continues, and the page syncs as a journal with no document.
|
|
114
|
+
*
|
|
115
|
+
* Every entry must name the same document type. The module reads that type
|
|
116
|
+
* off the spec rather than off a resolved template, and `links.mjs` has to
|
|
117
|
+
* reach the same answer with no lookup at all, so a list that disagrees with
|
|
118
|
+
* itself has no single answer to give.
|
|
119
|
+
*/
|
|
120
|
+
function normalizeFoundryBase(base, pagePath) {
|
|
121
|
+
const raw = Array.isArray(base) ? base : [base];
|
|
122
|
+
const specs = [];
|
|
123
|
+
for (const entry of raw) {
|
|
124
|
+
if (typeof entry !== "string" || entry.trim().length === 0) {
|
|
125
|
+
console.warn(` ${pagePath}: foundry.base entries must be non-empty strings (a UUID like `
|
|
126
|
+
+ `"Compendium.<pkg>.<pack>.Actor.<id>", or a type like "Actor:npc"); `
|
|
127
|
+
+ `got ${entry === null ? "null" : typeof entry}. Ignoring foundry.base — this page `
|
|
128
|
+
+ `will sync as a journal but create no document.`);
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
specs.push(entry.trim());
|
|
132
|
+
}
|
|
133
|
+
if (specs.length === 0) {
|
|
134
|
+
console.warn(` ${pagePath}: foundry.base is an empty list; ignoring.`);
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
const types = new Map(); // docName → first spec that named it
|
|
138
|
+
for (const spec of specs) {
|
|
139
|
+
// A Moulinette entry is deliberately typeless: only the reader's own
|
|
140
|
+
// library knows whether that asset is a Scene or an Actor, and this runs
|
|
141
|
+
// at build time with no library to ask.
|
|
142
|
+
if (spec.startsWith("@moulinette/"))
|
|
143
|
+
continue;
|
|
144
|
+
const docName = foundryBaseDocName(spec);
|
|
145
|
+
if (!docName) {
|
|
146
|
+
console.warn(` ${pagePath}: foundry.base entry "${spec}" names no document type; ignoring foundry.base.`);
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
if (!types.has(docName))
|
|
150
|
+
types.set(docName, spec);
|
|
151
|
+
}
|
|
152
|
+
if (types.size === 0) {
|
|
153
|
+
console.warn(` ${pagePath}: foundry.base names only Moulinette references, which carry no document `
|
|
154
|
+
+ `type. Add an entry naming the type (e.g. "Scene") — it is also what the page falls `
|
|
155
|
+
+ `back to for a reader without that pack. Ignoring foundry.base.`);
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
if (types.size > 1) {
|
|
159
|
+
const detail = [...types].map(([t, spec]) => `${t} (from "${spec}")`).join(", ");
|
|
160
|
+
console.warn(` ${pagePath}: every foundry.base entry must name the same document type, got ${detail}. `
|
|
161
|
+
+ `Ignoring foundry.base — this page will sync as a journal but create no document.`);
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
// A list whose last entry is a UUID can still fail on a world that lacks
|
|
165
|
+
// every package named. A blank-doc tail is what makes the chain total.
|
|
166
|
+
const last = specs[specs.length - 1];
|
|
167
|
+
if (specs.length > 1 && (last.includes(".") || last.startsWith("@moulinette/"))) {
|
|
168
|
+
console.warn(` ${pagePath}: foundry.base list ends with "${last}", so it can still resolve to nothing. `
|
|
169
|
+
+ `End with a blank-document entry (e.g. "${[...types.keys()][0]}:npc" or "${[...types.keys()][0]}") `
|
|
170
|
+
+ `to guarantee a document.`);
|
|
171
|
+
}
|
|
172
|
+
return specs.length === 1 ? specs[0] : specs;
|
|
173
|
+
}
|
|
174
|
+
export async function collectBodyMeta(p, vaultPath) {
|
|
175
|
+
const fm = p.frontmatter ?? {};
|
|
176
|
+
const out = { role: p.role };
|
|
177
|
+
const basename = p.path.split("/").pop().replace(/\.md$/i, "");
|
|
178
|
+
if (p.title && p.title !== basename)
|
|
179
|
+
out.title = p.title;
|
|
180
|
+
const fo = fm["foundry"];
|
|
181
|
+
if (fo && typeof fo === "object" && !Array.isArray(fo)) {
|
|
182
|
+
const block = {};
|
|
183
|
+
// `base` is one spec, or a priority list the module tries in order so a
|
|
184
|
+
// vault degrades across worlds with different content installed. A
|
|
185
|
+
// malformed base is dropped with a warning rather than failing the build,
|
|
186
|
+
// same as foundry.id below. Silence here is worse than it looks: the
|
|
187
|
+
// module never receives the key, so it can't report the page either, and
|
|
188
|
+
// the page syncs as a journal with no Actor/Item and no explanation.
|
|
189
|
+
const base = fo["base"];
|
|
190
|
+
if (base !== undefined && base !== null) {
|
|
191
|
+
const normalized = normalizeFoundryBase(base, p.path);
|
|
192
|
+
if (normalized !== null)
|
|
193
|
+
block.base = normalized;
|
|
194
|
+
}
|
|
195
|
+
const embed = fo["embed"];
|
|
196
|
+
if (typeof embed === "boolean")
|
|
197
|
+
block.embed = embed;
|
|
198
|
+
// foundry.sync: false keeps the page out of Foundry altogether — no
|
|
199
|
+
// JournalEntryPage, no derived doc. The page still renders on the wiki.
|
|
200
|
+
// Unlike `embed`, which only suppresses the article inside a derived
|
|
201
|
+
// doc's description, this drops the page from the sync set entirely.
|
|
202
|
+
const sync = fo["sync"];
|
|
203
|
+
if (typeof sync === "boolean")
|
|
204
|
+
block.sync = sync;
|
|
205
|
+
// foundry.journal: false makes the derived doc without the JournalEntryPage
|
|
206
|
+
// that normally accompanies it. For a page that exists to carry a Scene or
|
|
207
|
+
// an Actor and has no article worth reading in the sidebar.
|
|
208
|
+
const journal = fo["journal"];
|
|
209
|
+
if (typeof journal === "boolean")
|
|
210
|
+
block.journal = journal;
|
|
211
|
+
// foundry.link: "doc" makes wikilinks to this page resolve to the document
|
|
212
|
+
// it instantiates rather than to its journal page. Implied by
|
|
213
|
+
// `journal: false`, where there is no journal page to link to.
|
|
214
|
+
const link = fo["link"];
|
|
215
|
+
if (link === "doc" || link === "journal")
|
|
216
|
+
block.link = link;
|
|
217
|
+
const data = fo["data"];
|
|
218
|
+
if (data && typeof data === "object" && !Array.isArray(data))
|
|
219
|
+
block.data = data;
|
|
220
|
+
// foundry.folder: a "/"-separated folder path the instantiated doc is
|
|
221
|
+
// filed under, nested inside the vault's own sidebar folder. Absent
|
|
222
|
+
// means the vault folder itself, which is where everything used to land.
|
|
223
|
+
const folder = fo["folder"];
|
|
224
|
+
if (typeof folder === "string" && folder.trim().length > 0)
|
|
225
|
+
block.folder = folder.trim();
|
|
226
|
+
// foundry.id: an explicit Foundry document id for this page. When set,
|
|
227
|
+
// overrides the SHA1-derived id used for both the JournalEntryPage and
|
|
228
|
+
// (if foundry.base is present) the instantiated derived doc. Lets users
|
|
229
|
+
// hardcode UUIDs that other Foundry-side code (macros, scene flags,
|
|
230
|
+
// module integrations) needs to reference. Foundry ids are 16 chars from
|
|
231
|
+
// [A-Za-z0-9]; a malformed value is dropped with a warning rather than
|
|
232
|
+
// failing the build.
|
|
233
|
+
const idVal = fo["id"];
|
|
234
|
+
if (typeof idVal === "string") {
|
|
235
|
+
const trimmed = idVal.trim();
|
|
236
|
+
if (FOUNDRY_ID_RE.test(trimmed))
|
|
237
|
+
block.id = trimmed;
|
|
238
|
+
else if (trimmed.length > 0) {
|
|
239
|
+
console.warn(` ${p.path}: foundry.id "${trimmed}" is not a valid Foundry id (16 chars [A-Za-z0-9]); ignoring`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
// foundry.data_json: vault-relative path to a JSON file. Read + parse
|
|
243
|
+
// at build time and inline into the meta as `data_json`. The Foundry
|
|
244
|
+
// module deep-merges it onto the base doc BEFORE foundry.data, so a
|
|
245
|
+
// user can layer hand-tuned overrides on top of an exported sheet.
|
|
246
|
+
// Folding the parsed object into meta means the body-row hash already
|
|
247
|
+
// changes when the JSON content does — no separate change-detection.
|
|
248
|
+
const dataJsonPath = fo["data_json"];
|
|
249
|
+
if (typeof dataJsonPath === "string" && dataJsonPath.trim().length > 0) {
|
|
250
|
+
const parsed = await loadDataJson(vaultPath, dataJsonPath.trim(), p.path);
|
|
251
|
+
if (parsed !== null)
|
|
252
|
+
block.data_json = parsed;
|
|
253
|
+
}
|
|
254
|
+
if (Object.keys(block).length > 0)
|
|
255
|
+
out.foundry = block;
|
|
256
|
+
}
|
|
257
|
+
if (p.coverImage)
|
|
258
|
+
out.image = p.coverImage;
|
|
259
|
+
return out;
|
|
260
|
+
}
|
|
261
|
+
/** Read + parse a vault-relative JSON file referenced by `foundry.data_json`.
|
|
262
|
+
* Warns on missing / unparseable file and returns null so the page renders
|
|
263
|
+
* without the overlay rather than failing the build. */
|
|
264
|
+
export async function loadDataJson(vaultPath, relPath, pagePath) {
|
|
265
|
+
const abs = join(vaultPath, relPath);
|
|
266
|
+
try {
|
|
267
|
+
const raw = await readFile(abs, "utf8");
|
|
268
|
+
return JSON.parse(raw);
|
|
269
|
+
}
|
|
270
|
+
catch (err) {
|
|
271
|
+
const code = err.code;
|
|
272
|
+
if (code === "ENOENT") {
|
|
273
|
+
console.warn(` ${pagePath}: foundry.data_json "${relPath}" not found, skipping`);
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
console.warn(` ${pagePath}: foundry.data_json "${relPath}" failed to parse: ${err.message}`);
|
|
277
|
+
}
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
/** Foundry document ids: exactly 16 chars from [A-Za-z0-9]. Validated when
|
|
282
|
+
* authors set `foundry.id` to override the SHA1-derived default. */
|
|
283
|
+
const FOUNDRY_ID_RE = /^[A-Za-z0-9]{16}$/;
|
|
284
|
+
//# sourceMappingURL=foundry-meta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"foundry-meta.js","sourceRoot":"","sources":["../src/foundry-meta.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// Build a module's JournalEntry pack from the pages the wiki already rendered.
|
|
2
|
+
//
|
|
3
|
+
// Mirrors the sync model exactly: one JournalEntry per vault directory, every
|
|
4
|
+
// .md file in that directory an embedded JournalEntryPage. Matching it is the
|
|
5
|
+
// point — a document's description embeds a journal page, and if the module
|
|
6
|
+
// laid its journals out differently, the same vault would produce two
|
|
7
|
+
// different-shaped things depending on how a reader got it.
|
|
8
|
+
//
|
|
9
|
+
// The HTML is the wiki's own rendered body, not a second rendering of the
|
|
10
|
+
// markdown. That matters: a page's battlemap, statblock and `fm:` values only
|
|
11
|
+
// exist in the rendered output, so re-rendering the source with a plain
|
|
12
|
+
// markdown parser would quietly ship a different article than the one on the
|
|
13
|
+
// site.
|
|
14
|
+
import { createHash } from "node:crypto";
|
|
15
|
+
/** The sync path's id scheme, namespaced to the module instead of the vault. */
|
|
16
|
+
function det(kind, key) {
|
|
17
|
+
return createHash("sha1").update(`vaults:${kind}:${key}`).digest("hex").slice(0, 16);
|
|
18
|
+
}
|
|
19
|
+
export function folderOfPath(path) {
|
|
20
|
+
const i = path.lastIndexOf("/");
|
|
21
|
+
return i < 0 ? "" : path.slice(0, i);
|
|
22
|
+
}
|
|
23
|
+
/** One entry per directory, so every page in a folder shares an id. */
|
|
24
|
+
export const journalEntryId = (ns, path) => det("entry", `${ns}:${folderOfPath(path)}`);
|
|
25
|
+
export const journalPageId = (ns, path) => det("page", `${ns}:${path}`);
|
|
26
|
+
/**
|
|
27
|
+
* Rewrite the wiki's HTML into something a Foundry journal can render.
|
|
28
|
+
*
|
|
29
|
+
* Two things have to change and the rest is already fine. An internal link is
|
|
30
|
+
* a site path that means nothing inside Foundry, so it becomes a `@UUID`
|
|
31
|
+
* enricher pointing at the compendium page. A media src is a deploy-absolute
|
|
32
|
+
* path, so it becomes a module-relative one and the file is bundled.
|
|
33
|
+
*
|
|
34
|
+
* Deliberately narrower than the sync path's transform, which also converts
|
|
35
|
+
* role-gated callouts into Foundry secret blocks. A module carries only one
|
|
36
|
+
* role's pages, so there is no gated content inside them to convert.
|
|
37
|
+
*/
|
|
38
|
+
export function transformForModule(html, moduleId, targets, assets) {
|
|
39
|
+
// An internal link the renderer marked. Attribute order is not fixed — the
|
|
40
|
+
// renderer emits `class` before `href` in some cases and after in others —
|
|
41
|
+
// so match the whole tag and read the attributes out of it rather than
|
|
42
|
+
// assuming a shape. Anchoring on `href` first silently missed every link
|
|
43
|
+
// that happened to be written the other way round.
|
|
44
|
+
let out = html.replace(/<a\s+([^>]*)>([\s\S]*?)<\/a>/g, (whole, attrs, label) => {
|
|
45
|
+
if (!/\bclass="[^"]*\binternal\b/.test(attrs))
|
|
46
|
+
return whole;
|
|
47
|
+
const href = /href="([^"]*)"/.exec(attrs)?.[1];
|
|
48
|
+
if (!href || !href.startsWith("/"))
|
|
49
|
+
return whole;
|
|
50
|
+
const key = decodeURIComponent(href.replace(/^\//, "").split("#")[0] ?? "");
|
|
51
|
+
const target = targets.get(key) ?? targets.get(`${key}.md`);
|
|
52
|
+
if (!target)
|
|
53
|
+
return label; // page not in the module: keep the words, drop the dead link
|
|
54
|
+
// Strip tags from the label: an enricher's {…} is plain text.
|
|
55
|
+
const text = label.replace(/<[^>]+>/g, "").trim();
|
|
56
|
+
return `@UUID[${target.uuid}]{${text}}`;
|
|
57
|
+
});
|
|
58
|
+
// src="/attachments/x.webp" — deploy-absolute, meaningless in a module.
|
|
59
|
+
out = out.replace(/(\ssrc=")\/([^"]+)"/g, (_whole, prefix, path) => {
|
|
60
|
+
const rel = decodeURIComponent(path);
|
|
61
|
+
assets.add(rel);
|
|
62
|
+
return `${prefix}modules/${moduleId}/assets/${rel}"`;
|
|
63
|
+
});
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Group pages into one JournalEntry per directory.
|
|
68
|
+
*
|
|
69
|
+
* The entry's name is the directory's own name, and a root-level page lands in
|
|
70
|
+
* an entry named for the module — the same shape the sync path produces, where
|
|
71
|
+
* a directory becomes an entry and its files become that entry's pages.
|
|
72
|
+
*/
|
|
73
|
+
export function buildJournalEntries(sources, ns, rootName, stats) {
|
|
74
|
+
const byFolder = new Map();
|
|
75
|
+
for (const s of sources) {
|
|
76
|
+
const folder = folderOfPath(s.path);
|
|
77
|
+
const list = byFolder.get(folder) ?? [];
|
|
78
|
+
list.push(s);
|
|
79
|
+
byFolder.set(folder, list);
|
|
80
|
+
}
|
|
81
|
+
const entries = [];
|
|
82
|
+
for (const [folder, pages] of [...byFolder].sort(([a], [b]) => a.localeCompare(b))) {
|
|
83
|
+
const id = journalEntryId(ns, pages[0].path);
|
|
84
|
+
entries.push({
|
|
85
|
+
_id: id,
|
|
86
|
+
name: folder ? folder.split("/").pop() : rootName,
|
|
87
|
+
pages: pages.map((p, i) => {
|
|
88
|
+
const pid = journalPageId(ns, p.path);
|
|
89
|
+
return {
|
|
90
|
+
_id: pid,
|
|
91
|
+
name: p.title,
|
|
92
|
+
type: "text",
|
|
93
|
+
title: { show: true, level: 1 },
|
|
94
|
+
text: { format: 1, content: p.html },
|
|
95
|
+
sort: (i + 1) * 100000,
|
|
96
|
+
ownership: { default: -1 },
|
|
97
|
+
flags: {},
|
|
98
|
+
_stats: stats,
|
|
99
|
+
_key: `!journal.pages!${id}.${pid}`,
|
|
100
|
+
};
|
|
101
|
+
}),
|
|
102
|
+
folder: null,
|
|
103
|
+
sort: 0,
|
|
104
|
+
flags: {},
|
|
105
|
+
ownership: { default: 0 },
|
|
106
|
+
_stats: stats,
|
|
107
|
+
_key: `!journal!${id}`,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return entries;
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=foundry-module-journal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"foundry-module-journal.js","sourceRoot":"","sources":["../src/foundry-module-journal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Render a vault page's markdown body into Foundry-flavoured description HTML,
|
|
2
|
+
// and build a pack's compendium folders.
|
|
3
|
+
//
|
|
4
|
+
// Ported from the standalone compiler this replaced, so `vaults build --module` can
|
|
5
|
+
// replace it. A vault body is Obsidian/vaults markdown; a Foundry sheet wants
|
|
6
|
+
// HTML with native inline rolls and Compendium @UUID cross-links.
|
|
7
|
+
import { createHash } from "node:crypto";
|
|
8
|
+
import { marked } from "marked";
|
|
9
|
+
// Fenced blocks that are wiki chrome: the Foundry sheet renders the same
|
|
10
|
+
// mechanics natively, so carrying the block's source into a description would
|
|
11
|
+
// show the reader a code listing of something already on their screen.
|
|
12
|
+
const HANDLER_FENCES = new Set([
|
|
13
|
+
"spell-card", "item-card", "statblock", "statblock-fm", "gallery", "battlemap",
|
|
14
|
+
"rolltable", "dice", "download", "foundry-manifest",
|
|
15
|
+
]);
|
|
16
|
+
function dropHandlerFences(md) {
|
|
17
|
+
return md.replace(/^```([\w-]+)[^\n]*\n[\s\S]*?^```[ \t]*$/gm, (block, lang) => HANDLER_FENCES.has(lang) ? "" : block);
|
|
18
|
+
}
|
|
19
|
+
/** `dice: 2d6+1` in backticks becomes dnd5e's native inline roll. */
|
|
20
|
+
function diceToRolls(md) {
|
|
21
|
+
return md.replace(/`dice:\s*([^`]+?)`/gi, (_m, f) => `[[/r ${f.trim()}]]`);
|
|
22
|
+
}
|
|
23
|
+
function rewriteWikilinks(md, index) {
|
|
24
|
+
return md.replace(/\[\[([^\]|#]+)(?:#[^\]|]+)?(?:\|([^\]]+))?\]\]/g, (_m, target, label) => {
|
|
25
|
+
const entry = index.get(target.trim().toLowerCase());
|
|
26
|
+
const text = (label ?? target).trim();
|
|
27
|
+
return entry ? `@UUID[${entry.uuid}]{${text}}` : text;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Wikilinks are rewritten before dice, not after: at that point a `dice:` span
|
|
32
|
+
* is still inside backticks, so the wikilink pass cannot mistake the `[[/r …]]`
|
|
33
|
+
* this would otherwise have produced for a link of its own.
|
|
34
|
+
*/
|
|
35
|
+
export function renderBody(md, index) {
|
|
36
|
+
let out = dropHandlerFences(md);
|
|
37
|
+
out = rewriteWikilinks(out, index);
|
|
38
|
+
out = diceToRolls(out);
|
|
39
|
+
return marked.parse(out, { async: false }).trim();
|
|
40
|
+
}
|
|
41
|
+
function folderId(packName, pathStr) {
|
|
42
|
+
return createHash("sha1").update(`folder:${packName}:${pathStr}`).digest("base64")
|
|
43
|
+
.replace(/[^A-Za-z0-9]/g, "").slice(0, 16).padEnd(16, "0");
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Build `!folders!` documents for a pack, plus the leaf folder each entry
|
|
47
|
+
* belongs in. `fvtt package pack` turns these keys into real compendium
|
|
48
|
+
* folders, so a pack of 500 items is browsable rather than one flat list.
|
|
49
|
+
*/
|
|
50
|
+
export function buildFolders(entries, packName, docType, stats) {
|
|
51
|
+
const docs = new Map();
|
|
52
|
+
const leafFor = new Map();
|
|
53
|
+
const ensure = (segments) => {
|
|
54
|
+
let parent = null;
|
|
55
|
+
let pathStr = "";
|
|
56
|
+
for (const seg of segments) {
|
|
57
|
+
pathStr = pathStr ? `${pathStr}/${seg}` : seg;
|
|
58
|
+
if (!docs.has(pathStr)) {
|
|
59
|
+
const id = folderId(packName, pathStr);
|
|
60
|
+
docs.set(pathStr, {
|
|
61
|
+
_id: id, name: seg, type: docType, folder: parent,
|
|
62
|
+
sort: 0, color: null, sorting: "a", flags: {},
|
|
63
|
+
_stats: stats, _key: `!folders!${id}`,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
parent = docs.get(pathStr)._id;
|
|
67
|
+
}
|
|
68
|
+
return parent;
|
|
69
|
+
};
|
|
70
|
+
for (const e of entries) {
|
|
71
|
+
leafFor.set(e.key, ensure(e.folderPath.split("/").map((s) => s.trim()).filter(Boolean)));
|
|
72
|
+
}
|
|
73
|
+
return { folderDocs: [...docs.values()], leafFor };
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=foundry-module-render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"foundry-module-render.js","sourceRoot":"","sources":["../src/foundry-module-render.ts"],"names":[],"mappings":"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"}
|