@kenjura/ursa 0.93.0 → 0.95.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/CHANGELOG.md +32 -0
- package/README.md +67 -0
- package/bin/ursa.js +14 -3
- package/package.json +1 -1
- package/src/dev.js +29 -0
- package/src/helper/__test__/folderConfig.test.js +89 -0
- package/src/helper/automenu.js +4 -2
- package/src/helper/build/__test__/autoIndex.test.js +67 -0
- package/src/helper/build/autoIndex.js +22 -5
- package/src/helper/folderConfig.js +34 -4
- package/src/jobs/__test__/generateJsonOnly.test.js +154 -0
- package/src/jobs/generate.js +285 -178
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `generate({ _jsonOnly: true })` — emit the data files and nothing else.
|
|
3
|
+
*
|
|
4
|
+
* The load-bearing claim is not "fewer files": it is that the .json a JSON-only
|
|
5
|
+
* build writes is byte-identical to the one a full build writes. Every step the
|
|
6
|
+
* mode skips operates on the assembled page, never on the JSON. If that ever
|
|
7
|
+
* stops being true, `identical to a full build's JSON` fails here rather than
|
|
8
|
+
* silently shipping different data to a consumer.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
import { mkdtemp, mkdir, writeFile, readFile, rm } from "fs/promises";
|
|
13
|
+
import { existsSync } from "fs";
|
|
14
|
+
import { tmpdir } from "os";
|
|
15
|
+
import { generate } from "../generate.js";
|
|
16
|
+
import { clearConfigCache } from "../../helper/folderConfig.js";
|
|
17
|
+
|
|
18
|
+
const META = join(process.cwd(), "meta");
|
|
19
|
+
|
|
20
|
+
let source;
|
|
21
|
+
let output;
|
|
22
|
+
|
|
23
|
+
async function doc(relPath, contents) {
|
|
24
|
+
const full = join(source, relPath);
|
|
25
|
+
await mkdir(join(full, ".."), { recursive: true });
|
|
26
|
+
await writeFile(full, contents);
|
|
27
|
+
return full;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
beforeEach(async () => {
|
|
31
|
+
source = await mkdtemp(join(tmpdir(), "ursa-jsononly-src-"));
|
|
32
|
+
output = await mkdtemp(join(tmpdir(), "ursa-jsononly-out-"));
|
|
33
|
+
clearConfigCache();
|
|
34
|
+
|
|
35
|
+
await doc("index.md", "# Home\n\nWelcome.\n");
|
|
36
|
+
await doc(
|
|
37
|
+
"character/powers/absorb-magic.md",
|
|
38
|
+
[
|
|
39
|
+
"---",
|
|
40
|
+
"class: Witch",
|
|
41
|
+
"name: Absorb Magic",
|
|
42
|
+
"school: Antimagic",
|
|
43
|
+
"brief: Absorb energy from a touched spell",
|
|
44
|
+
"---",
|
|
45
|
+
"",
|
|
46
|
+
"# Absorb Magic",
|
|
47
|
+
"",
|
|
48
|
+
"Touch a spell and take it apart.",
|
|
49
|
+
"",
|
|
50
|
+
"## Range",
|
|
51
|
+
"",
|
|
52
|
+
"Touch.",
|
|
53
|
+
"",
|
|
54
|
+
].join("\n")
|
|
55
|
+
);
|
|
56
|
+
await doc("character/powers/mind-blast.md", "# Mind Blast\n\nA blast, of the mind.\n");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
afterEach(async () => {
|
|
60
|
+
await rm(source, { recursive: true, force: true });
|
|
61
|
+
await rm(output, { recursive: true, force: true });
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const run = (opts) =>
|
|
65
|
+
generate({ _source: source, _meta: META, _output: output, _clean: true, ...opts });
|
|
66
|
+
|
|
67
|
+
describe("generate --json-only", () => {
|
|
68
|
+
it("writes the document JSON", async () => {
|
|
69
|
+
await run({ _jsonOnly: true });
|
|
70
|
+
expect(existsSync(join(output, "character/powers/absorb-magic.json"))).toBe(true);
|
|
71
|
+
expect(existsSync(join(output, "index.json"))).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("writes the directory record lists, which are the point of the mode", async () => {
|
|
75
|
+
await run({ _jsonOnly: true });
|
|
76
|
+
const listPath = join(output, "character/powers.json");
|
|
77
|
+
expect(existsSync(listPath)).toBe(true);
|
|
78
|
+
|
|
79
|
+
const records = JSON.parse(await readFile(listPath, "utf8"));
|
|
80
|
+
const absorb = records.find((r) => r.name === "absorb-magic");
|
|
81
|
+
expect(absorb).toBeDefined();
|
|
82
|
+
expect(absorb.url).toBe("/character/powers/absorb-magic.html");
|
|
83
|
+
expect(absorb.metadata.school).toBe("Antimagic");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("writes no HTML and no XML", async () => {
|
|
87
|
+
await run({ _jsonOnly: true });
|
|
88
|
+
expect(existsSync(join(output, "index.html"))).toBe(false);
|
|
89
|
+
expect(existsSync(join(output, "character/powers/absorb-magic.html"))).toBe(false);
|
|
90
|
+
expect(existsSync(join(output, "character/powers/absorb-magic.xml"))).toBe(false);
|
|
91
|
+
// The directory listing page, distinct from the record list above.
|
|
92
|
+
expect(existsSync(join(output, "character/powers.html"))).toBe(false);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("writes no meta assets, search index, menu data or recent activity", async () => {
|
|
96
|
+
await run({ _jsonOnly: true });
|
|
97
|
+
expect(existsSync(join(output, "public", "search-index.json"))).toBe(false);
|
|
98
|
+
expect(existsSync(join(output, "public", "fulltext-index.json"))).toBe(false);
|
|
99
|
+
expect(existsSync(join(output, "public", "menu-data.json"))).toBe(false);
|
|
100
|
+
expect(existsSync(join(output, "public", "recent-activity.json"))).toBe(false);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("produces JSON identical to a full build's", async () => {
|
|
104
|
+
await run({ _jsonOnly: true });
|
|
105
|
+
const jsonOnly = await readFile(
|
|
106
|
+
join(output, "character/powers/absorb-magic.json"),
|
|
107
|
+
"utf8"
|
|
108
|
+
);
|
|
109
|
+
const jsonOnlyList = await readFile(join(output, "character/powers.json"), "utf8");
|
|
110
|
+
|
|
111
|
+
await rm(output, { recursive: true, force: true });
|
|
112
|
+
await mkdir(output, { recursive: true });
|
|
113
|
+
await run({ _jsonOnly: false });
|
|
114
|
+
|
|
115
|
+
const full = await readFile(join(output, "character/powers/absorb-magic.json"), "utf8");
|
|
116
|
+
const fullList = await readFile(join(output, "character/powers.json"), "utf8");
|
|
117
|
+
|
|
118
|
+
expect(jsonOnly).toBe(full);
|
|
119
|
+
expect(jsonOnlyList).toBe(fullList);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("still emits everything on a normal build", async () => {
|
|
123
|
+
await run({ _jsonOnly: false });
|
|
124
|
+
expect(existsSync(join(output, "character/powers/absorb-magic.html"))).toBe(true);
|
|
125
|
+
expect(existsSync(join(output, "character/powers/absorb-magic.xml"))).toBe(true);
|
|
126
|
+
expect(existsSync(join(output, "public", "menu-data.json"))).toBe(true);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe("mixing modes against one source tree", () => {
|
|
131
|
+
// The hash cache lives in the SOURCE tree and is shared by both modes, so the
|
|
132
|
+
// per-document output check has to be mode-aware or one mode's cache entries
|
|
133
|
+
// would convince the other that its own missing outputs are up to date.
|
|
134
|
+
|
|
135
|
+
it("a full build after a JSON-only build still writes the HTML", async () => {
|
|
136
|
+
await run({ _jsonOnly: true });
|
|
137
|
+
expect(existsSync(join(output, "character/powers/absorb-magic.html"))).toBe(false);
|
|
138
|
+
|
|
139
|
+
// Warm: no --clean, so the hash cache from the JSON-only run is in play.
|
|
140
|
+
await generate({ _source: source, _meta: META, _output: output, _jsonOnly: false });
|
|
141
|
+
expect(existsSync(join(output, "character/powers/absorb-magic.html"))).toBe(true);
|
|
142
|
+
expect(existsSync(join(output, "character/powers/absorb-magic.xml"))).toBe(true);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("a JSON-only build after a full build leaves the JSON in place", async () => {
|
|
146
|
+
await run({ _jsonOnly: false });
|
|
147
|
+
const before = await readFile(join(output, "character/powers/absorb-magic.json"), "utf8");
|
|
148
|
+
|
|
149
|
+
await generate({ _source: source, _meta: META, _output: output, _jsonOnly: true });
|
|
150
|
+
const after = await readFile(join(output, "character/powers/absorb-magic.json"), "utf8");
|
|
151
|
+
|
|
152
|
+
expect(after).toBe(before);
|
|
153
|
+
});
|
|
154
|
+
});
|