@forumulate/cli 0.3.6 → 0.4.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/README.md +4 -3
- package/dist/cli.js +25 -1
- package/dist/cli.js.map +1 -1
- package/dist/publish/api.d.ts +95 -0
- package/dist/publish/api.d.ts.map +1 -0
- package/dist/publish/api.js +265 -0
- package/dist/publish/api.js.map +1 -0
- package/dist/publish/command.d.ts +44 -0
- package/dist/publish/command.d.ts.map +1 -0
- package/dist/publish/command.js +246 -0
- package/dist/publish/command.js.map +1 -0
- package/dist/publish/compare.d.ts +45 -0
- package/dist/publish/compare.d.ts.map +1 -0
- package/dist/publish/compare.js +124 -0
- package/dist/publish/compare.js.map +1 -0
- package/dist/publish/context.d.ts +46 -0
- package/dist/publish/context.d.ts.map +1 -0
- package/dist/publish/context.js +54 -0
- package/dist/publish/context.js.map +1 -0
- package/dist/publish/docs-apply.d.ts +39 -0
- package/dist/publish/docs-apply.d.ts.map +1 -0
- package/dist/publish/docs-apply.js +178 -0
- package/dist/publish/docs-apply.js.map +1 -0
- package/dist/publish/docs-plan.d.ts +40 -0
- package/dist/publish/docs-plan.d.ts.map +1 -0
- package/dist/publish/docs-plan.js +211 -0
- package/dist/publish/docs-plan.js.map +1 -0
- package/dist/publish/docs-reconcile.d.ts +26 -0
- package/dist/publish/docs-reconcile.d.ts.map +1 -0
- package/dist/publish/docs-reconcile.js +162 -0
- package/dist/publish/docs-reconcile.js.map +1 -0
- package/dist/publish/extras.d.ts +25 -0
- package/dist/publish/extras.d.ts.map +1 -0
- package/dist/publish/extras.js +218 -0
- package/dist/publish/extras.js.map +1 -0
- package/dist/publish/frontmatter.d.ts +45 -0
- package/dist/publish/frontmatter.d.ts.map +1 -0
- package/dist/publish/frontmatter.js +265 -0
- package/dist/publish/frontmatter.js.map +1 -0
- package/dist/publish/manifest.d.ts +5 -0
- package/dist/publish/manifest.d.ts.map +1 -0
- package/dist/publish/manifest.js +144 -0
- package/dist/publish/manifest.js.map +1 -0
- package/dist/publish/posts.d.ts +39 -0
- package/dist/publish/posts.d.ts.map +1 -0
- package/dist/publish/posts.js +350 -0
- package/dist/publish/posts.js.map +1 -0
- package/dist/publish/rewrite.d.ts +20 -0
- package/dist/publish/rewrite.d.ts.map +1 -0
- package/dist/publish/rewrite.js +82 -0
- package/dist/publish/rewrite.js.map +1 -0
- package/dist/publish/scan.d.ts +25 -0
- package/dist/publish/scan.d.ts.map +1 -0
- package/dist/publish/scan.js +351 -0
- package/dist/publish/scan.js.map +1 -0
- package/dist/publish/types.d.ts +155 -0
- package/dist/publish/types.d.ts.map +1 -0
- package/dist/publish/types.js +13 -0
- package/dist/publish/types.js.map +1 -0
- package/dist/run.d.ts +10 -1
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +58 -45
- package/dist/run.js.map +1 -1
- package/package.json +3 -2
- package/dist/publish-dir.d.ts +0 -54
- package/dist/publish-dir.d.ts.map +0 -1
- package/dist/publish-dir.js +0 -119
- package/dist/publish-dir.js.map +0 -1
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.payloadOf = payloadOf;
|
|
4
|
+
exports.uploadAssetsFor = uploadAssetsFor;
|
|
5
|
+
exports.applyDocs = applyDocs;
|
|
6
|
+
exports.dryRunImports = dryRunImports;
|
|
7
|
+
exports.applyPrune = applyPrune;
|
|
8
|
+
exports.knownUrls = knownUrls;
|
|
9
|
+
const frontmatter_1 = require("./frontmatter");
|
|
10
|
+
const api_1 = require("./api");
|
|
11
|
+
const context_1 = require("./context");
|
|
12
|
+
const rewrite_1 = require("./rewrite");
|
|
13
|
+
/**
|
|
14
|
+
* The writes a docs plan names, in the only order that works: collections
|
|
15
|
+
* before the pages filed in them, moves before the import that would otherwise
|
|
16
|
+
* refuse a page living in another collection, assets before the text that
|
|
17
|
+
* points at them.
|
|
18
|
+
*
|
|
19
|
+
* The bodies go through `POST …/docs/import` — the product's own converter,
|
|
20
|
+
* the door our own guides are published through — not page-by-page creates:
|
|
21
|
+
* it is the one place that understands every Markdown dialect we accept, and
|
|
22
|
+
* it publishes as it writes.
|
|
23
|
+
*/
|
|
24
|
+
/** Files per import call. Long calls are cut at the edge at 30 s while the work carries on. */
|
|
25
|
+
const BATCH = 20;
|
|
26
|
+
/** The text sent for one page: its address declared, links and assets pointed at the web. */
|
|
27
|
+
function payloadOf(ctx, page, urls) {
|
|
28
|
+
if (page.stub)
|
|
29
|
+
return page.source;
|
|
30
|
+
const linked = (0, rewrite_1.rewriteLinks)(page.source, page.file, ctx.scan.slugByFile);
|
|
31
|
+
return (0, frontmatter_1.declareSlug)((0, rewrite_1.rewriteAssets)(linked, urls), page.slug);
|
|
32
|
+
}
|
|
33
|
+
/** Upload what the given files reference; the map is ref → URL, per file. */
|
|
34
|
+
async function uploadAssetsFor(ctx, files, existing) {
|
|
35
|
+
const out = new Map();
|
|
36
|
+
const seen = new Map();
|
|
37
|
+
for (const file of files) {
|
|
38
|
+
const refs = ctx.scan.refs.get(file);
|
|
39
|
+
if (!refs)
|
|
40
|
+
continue;
|
|
41
|
+
const urls = new Map();
|
|
42
|
+
for (const [ref, abs] of refs) {
|
|
43
|
+
let url = seen.get(abs);
|
|
44
|
+
if (!url) {
|
|
45
|
+
const had = existing.size;
|
|
46
|
+
url = await ctx.api.upload(ctx.org, ctx.spaceId, abs, existing);
|
|
47
|
+
if (existing.size > had)
|
|
48
|
+
ctx.line({ verb: 'upload', what: abs.slice(ctx.root.length + 1) || abs, detail: url });
|
|
49
|
+
seen.set(abs, url);
|
|
50
|
+
}
|
|
51
|
+
urls.set(ref, url);
|
|
52
|
+
}
|
|
53
|
+
out.set(file, urls);
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
/** Step 1-4: collections, moves, assets, bodies. Returns the asset URLs it used. */
|
|
58
|
+
async function applyDocs(ctx, plan, existingAssets) {
|
|
59
|
+
const ids = new Map();
|
|
60
|
+
for (const [c, live] of plan.collectionOf)
|
|
61
|
+
if (live)
|
|
62
|
+
ids.set(c, live.id);
|
|
63
|
+
for (const c of plan.createCollections) {
|
|
64
|
+
const { collection } = await ctx.api.post((0, context_1.spacePath)(ctx, '/collections'), {
|
|
65
|
+
name: c.name,
|
|
66
|
+
slug: c.slug,
|
|
67
|
+
...(c.nav ? { nav: c.nav } : {}),
|
|
68
|
+
});
|
|
69
|
+
ids.set(c, collection.id);
|
|
70
|
+
plan.collectionOf.set(c, collection);
|
|
71
|
+
}
|
|
72
|
+
for (const { live, body } of plan.patchCollections)
|
|
73
|
+
await ctx.api.patch((0, context_1.spacePath)(ctx, `/collections/${live.id}`), body);
|
|
74
|
+
for (const { page, to } of plan.moves) {
|
|
75
|
+
await ctx.api.post((0, context_1.spacePath)(ctx, `/pages/${page.id}/reorder`), { collectionId: ids.get(to) });
|
|
76
|
+
}
|
|
77
|
+
// Every file that is sent, and every page whose metadata names a local
|
|
78
|
+
// image, needs its assets before anything points at them.
|
|
79
|
+
const files = new Set();
|
|
80
|
+
for (const sends of plan.sends.values())
|
|
81
|
+
for (const s of sends)
|
|
82
|
+
files.add(s.page.file);
|
|
83
|
+
for (const c of ctx.scan.collections)
|
|
84
|
+
for (const p of c.pages)
|
|
85
|
+
if (Object.keys(p.meta).length && ctx.only.piece(ctx.spec, p.slug, p.file, c))
|
|
86
|
+
files.add(p.file);
|
|
87
|
+
const urls = await uploadAssetsFor(ctx, [...files], existingAssets);
|
|
88
|
+
for (const [c, sends] of plan.sends) {
|
|
89
|
+
const collectionId = ids.get(c);
|
|
90
|
+
for (let i = 0; i < sends.length; i += BATCH) {
|
|
91
|
+
const batch = sends.slice(i, i + BATCH);
|
|
92
|
+
const report = await ctx.api.post((0, context_1.spacePath)(ctx, '/docs/import'), {
|
|
93
|
+
files: batch.map((s) => ({
|
|
94
|
+
name: s.page.importName,
|
|
95
|
+
content: payloadOf(ctx, s.page, urls.get(s.page.file) ?? new Map()),
|
|
96
|
+
})),
|
|
97
|
+
collectionId,
|
|
98
|
+
onExisting: 'replace',
|
|
99
|
+
});
|
|
100
|
+
noteImport(ctx, c, report);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return urls;
|
|
104
|
+
}
|
|
105
|
+
/** A dry run with a token asks the importer itself, so the plan is the server's too. */
|
|
106
|
+
async function dryRunImports(ctx, plan) {
|
|
107
|
+
for (const [c, sends] of plan.sends) {
|
|
108
|
+
const live = plan.collectionOf.get(c);
|
|
109
|
+
const urls = (file) => {
|
|
110
|
+
const refs = ctx.scan.refs.get(file);
|
|
111
|
+
const m = new Map();
|
|
112
|
+
// Not uploaded on a dry run: a placeholder keeps the reference a URL.
|
|
113
|
+
if (refs)
|
|
114
|
+
for (const ref of refs.keys())
|
|
115
|
+
m.set(ref, `https://assets.invalid/${encodeURIComponent(ref)}`);
|
|
116
|
+
return m;
|
|
117
|
+
};
|
|
118
|
+
for (let i = 0; i < sends.length; i += BATCH) {
|
|
119
|
+
const batch = sends.slice(i, i + BATCH);
|
|
120
|
+
// `dryRun: true` is the importer's own promise to write nothing, so this
|
|
121
|
+
// one POST goes around the read-only guard on purpose.
|
|
122
|
+
const res = await ctx.api.opts.client
|
|
123
|
+
.request('POST', (0, context_1.spacePath)(ctx, '/docs/import'), {
|
|
124
|
+
body: {
|
|
125
|
+
files: batch.map((s) => ({
|
|
126
|
+
name: s.page.importName,
|
|
127
|
+
content: payloadOf(ctx, s.page, urls(s.page.file)),
|
|
128
|
+
})),
|
|
129
|
+
...(live ? { collectionId: live.id } : { collection: c.name }),
|
|
130
|
+
onExisting: 'replace',
|
|
131
|
+
dryRun: true,
|
|
132
|
+
},
|
|
133
|
+
})
|
|
134
|
+
.catch((e) => {
|
|
135
|
+
ctx.errors.push(`import dry run for ${c.slug}: ${e.status ?? ''} ${e.message}`);
|
|
136
|
+
return null;
|
|
137
|
+
});
|
|
138
|
+
if (res)
|
|
139
|
+
noteImport(ctx, c, res);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function noteImport(ctx, c, report) {
|
|
144
|
+
for (const f of report.failures ?? [])
|
|
145
|
+
ctx.errors.push(`${c.dir || '(root)'}/${f.file}: ${f.reason}`);
|
|
146
|
+
if ((report.failed ?? 0) > (report.failures?.length ?? 0))
|
|
147
|
+
ctx.errors.push(`${c.slug}: ${report.failed} file(s) failed to import`);
|
|
148
|
+
for (const s of report.refused ?? [])
|
|
149
|
+
ctx.errors.push(`${s}: refused by the importer`);
|
|
150
|
+
for (const b of report.brokenLinks ?? [])
|
|
151
|
+
ctx.line({ verb: 'warn', what: `page ${b.pageSlug}`, detail: `link to nothing: ${b.url}` });
|
|
152
|
+
}
|
|
153
|
+
/** `--prune`, last: pages to the trash, then the collections left empty. */
|
|
154
|
+
async function applyPrune(ctx, plan) {
|
|
155
|
+
if (plan.prunePages.length)
|
|
156
|
+
await ctx.api.post((0, context_1.spacePath)(ctx, '/pages/bulk-delete'), {
|
|
157
|
+
ids: plan.prunePages.map((p) => p.id),
|
|
158
|
+
});
|
|
159
|
+
for (const c of plan.pruneCollections)
|
|
160
|
+
await ctx.api.del((0, context_1.spacePath)(ctx, `/collections/${c.id}`));
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Per file: local reference → the URL it already has, for assets uploaded on
|
|
164
|
+
* an earlier run. A reference not uploaded yet keeps its local spelling, which
|
|
165
|
+
* is what makes a plan say "coverUrl would change" — because it would.
|
|
166
|
+
*/
|
|
167
|
+
function knownUrls(ctx, existing) {
|
|
168
|
+
return (file) => {
|
|
169
|
+
const out = new Map();
|
|
170
|
+
for (const [ref, abs] of ctx.scan.refs.get(file) ?? []) {
|
|
171
|
+
const url = existing.get((0, api_1.assetName)(abs));
|
|
172
|
+
if (url)
|
|
173
|
+
out.set(ref, url);
|
|
174
|
+
}
|
|
175
|
+
return out;
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=docs-apply.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs-apply.js","sourceRoot":"","sources":["../../src/publish/docs-apply.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { SpaceCtx } from './context';
|
|
2
|
+
import type { LiveCollection, LivePage, LocalCollection, LocalPage } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* What publishing a docs folder would WRITE: collections, page bodies, moves
|
|
5
|
+
* between collections and the prune. Metadata and order come after the write
|
|
6
|
+
* (`docs-reconcile.ts`), because they need the ids of pages this run creates.
|
|
7
|
+
*/
|
|
8
|
+
export interface DocsPlan {
|
|
9
|
+
/** Local collection → the live one it is, or undefined when it will be created. */
|
|
10
|
+
collectionOf: Map<LocalCollection, LiveCollection | undefined>;
|
|
11
|
+
createCollections: LocalCollection[];
|
|
12
|
+
patchCollections: {
|
|
13
|
+
live: LiveCollection;
|
|
14
|
+
body: Record<string, unknown>;
|
|
15
|
+
}[];
|
|
16
|
+
/** A page that exists in another collection than its folder says. */
|
|
17
|
+
moves: {
|
|
18
|
+
page: LivePage;
|
|
19
|
+
to: LocalCollection;
|
|
20
|
+
}[];
|
|
21
|
+
/** Per collection, the files to send to the importer. */
|
|
22
|
+
sends: Map<LocalCollection, {
|
|
23
|
+
page: LocalPage;
|
|
24
|
+
why: 'create' | 'update';
|
|
25
|
+
}[]>;
|
|
26
|
+
prunePages: LivePage[];
|
|
27
|
+
pruneCollections: LiveCollection[];
|
|
28
|
+
unchanged: number;
|
|
29
|
+
}
|
|
30
|
+
export interface DocsLive {
|
|
31
|
+
collections: LiveCollection[];
|
|
32
|
+
pages: LivePage[];
|
|
33
|
+
publicRows: Map<string, Record<string, unknown>>;
|
|
34
|
+
}
|
|
35
|
+
/** The live collection a folder is: by slug, then by name. */
|
|
36
|
+
export declare function matchCollections(local: LocalCollection[], live: LiveCollection[], rootName?: string): Map<LocalCollection, LiveCollection | undefined>;
|
|
37
|
+
/** How a live page's body is read for comparison: one walk authenticated, per page otherwise. */
|
|
38
|
+
export type BodyOf = (page: LivePage) => Promise<string | null>;
|
|
39
|
+
export declare function planDocs(ctx: SpaceCtx, live: DocsLive, bodyOf: BodyOf, openapiCollections: Set<string>): Promise<DocsPlan>;
|
|
40
|
+
//# sourceMappingURL=docs-plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs-plan.d.ts","sourceRoot":"","sources":["../../src/publish/docs-plan.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEpF;;;;GAIG;AACH,MAAM,WAAW,QAAQ;IACvB,mFAAmF;IACnF,YAAY,EAAE,GAAG,CAAC,eAAe,EAAE,cAAc,GAAG,SAAS,CAAC,CAAC;IAC/D,iBAAiB,EAAE,eAAe,EAAE,CAAC;IACrC,gBAAgB,EAAE;QAAE,IAAI,EAAE,cAAc,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,EAAE,CAAC;IAC5E,qEAAqE;IACrE,KAAK,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,EAAE,EAAE,eAAe,CAAA;KAAE,EAAE,CAAC;IACjD,yDAAyD;IACzD,KAAK,EAAE,GAAG,CAAC,eAAe,EAAE;QAAE,IAAI,EAAE,SAAS,CAAC;QAAC,GAAG,EAAE,QAAQ,GAAG,QAAQ,CAAA;KAAE,EAAE,CAAC,CAAC;IAC7E,UAAU,EAAE,QAAQ,EAAE,CAAC;IACvB,gBAAgB,EAAE,cAAc,EAAE,CAAC;IACnC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAClD;AAED,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,eAAe,EAAE,EACxB,IAAI,EAAE,cAAc,EAAE,EACtB,QAAQ,CAAC,EAAE,MAAM,GAChB,GAAG,CAAC,eAAe,EAAE,cAAc,GAAG,SAAS,CAAC,CAwBlD;AAED,iGAAiG;AACjG,MAAM,MAAM,MAAM,GAAG,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAEhE,wBAAsB,QAAQ,CAC5B,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,MAAM,EACd,kBAAkB,EAAE,GAAG,CAAC,MAAM,CAAC,GAC9B,OAAO,CAAC,QAAQ,CAAC,CA0GnB"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.matchCollections = matchCollections;
|
|
4
|
+
exports.planDocs = planDocs;
|
|
5
|
+
const compare_1 = require("./compare");
|
|
6
|
+
/** The live collection a folder is: by slug, then by name. */
|
|
7
|
+
function matchCollections(local, live, rootName) {
|
|
8
|
+
const out = new Map();
|
|
9
|
+
const taken = new Set();
|
|
10
|
+
for (const c of local) {
|
|
11
|
+
let hit;
|
|
12
|
+
if (c.dir === '' && !rootName) {
|
|
13
|
+
// Files at the root of the space go where the space files loose pages.
|
|
14
|
+
hit = live.find((l) => l.isDefault) ?? live.find((l) => l.slug === 'pages');
|
|
15
|
+
if (!hit) {
|
|
16
|
+
c.slug = 'pages';
|
|
17
|
+
c.name = 'Pages';
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
c.slug = hit.slug;
|
|
21
|
+
c.name = hit.name;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
hit =
|
|
26
|
+
live.find((l) => !taken.has(l.id) && l.slug === c.slug) ??
|
|
27
|
+
live.find((l) => !taken.has(l.id) && l.name.toLowerCase() === c.name.toLowerCase());
|
|
28
|
+
}
|
|
29
|
+
if (hit)
|
|
30
|
+
taken.add(hit.id);
|
|
31
|
+
out.set(c, hit);
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
async function planDocs(ctx, live, bodyOf, openapiCollections) {
|
|
36
|
+
const { scan, spec, only } = ctx;
|
|
37
|
+
const collectionOf = matchCollections(scan.collections, live.collections, spec.rootCollection);
|
|
38
|
+
const plan = {
|
|
39
|
+
collectionOf,
|
|
40
|
+
createCollections: [],
|
|
41
|
+
patchCollections: [],
|
|
42
|
+
moves: [],
|
|
43
|
+
sends: new Map(),
|
|
44
|
+
prunePages: [],
|
|
45
|
+
pruneCollections: [],
|
|
46
|
+
unchanged: 0,
|
|
47
|
+
};
|
|
48
|
+
const bySlug = new Map(live.pages.map((p) => [p.slug, p]));
|
|
49
|
+
// Every slug is one address in a space: two files claiming it is a conflict
|
|
50
|
+
// the author resolves, not one the publisher picks a winner in.
|
|
51
|
+
const owner = new Map();
|
|
52
|
+
for (const c of scan.collections)
|
|
53
|
+
for (const p of c.pages) {
|
|
54
|
+
const prev = owner.get(p.slug);
|
|
55
|
+
if (prev)
|
|
56
|
+
ctx.errors.push(`two files publish at "${p.slug}": ${prev} and ${p.file} — give one a \`slug:\``);
|
|
57
|
+
owner.set(p.slug, p.file);
|
|
58
|
+
}
|
|
59
|
+
for (const c of scan.collections) {
|
|
60
|
+
const inScope = (p) => only.piece(spec, p.slug, p.file, c);
|
|
61
|
+
const scoped = c.pages.filter(inScope);
|
|
62
|
+
const target = collectionOf.get(c);
|
|
63
|
+
if (!scoped.length && only.all === false)
|
|
64
|
+
continue;
|
|
65
|
+
if (!target) {
|
|
66
|
+
plan.createCollections.push(c);
|
|
67
|
+
ctx.line({
|
|
68
|
+
verb: 'create',
|
|
69
|
+
what: `collection ${c.slug}`,
|
|
70
|
+
detail: `"${c.name}" ← ${c.dir || '(space root)'}/`,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
const declared = spec.collections?.[c.dir];
|
|
75
|
+
const body = {};
|
|
76
|
+
// Only what the author SAID. A name derived from a folder must not
|
|
77
|
+
// overwrite one somebody chose in the studio on every run.
|
|
78
|
+
if (declared?.name && declared.name !== target.name)
|
|
79
|
+
body.name = declared.name;
|
|
80
|
+
if (c.nav && JSON.stringify({ ...target.nav, ...c.nav }) !== JSON.stringify(target.nav ?? {}))
|
|
81
|
+
body.nav = c.nav;
|
|
82
|
+
if (Object.keys(body).length) {
|
|
83
|
+
plan.patchCollections.push({ live: target, body });
|
|
84
|
+
ctx.line({
|
|
85
|
+
verb: 'patch',
|
|
86
|
+
what: `collection ${target.slug}`,
|
|
87
|
+
detail: Object.keys(body).join(', '),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const sends = [];
|
|
92
|
+
for (const p of scoped) {
|
|
93
|
+
const existing = bySlug.get(p.slug);
|
|
94
|
+
if (!existing) {
|
|
95
|
+
sends.push({ page: p, why: 'create' });
|
|
96
|
+
ctx.line({ verb: 'create', what: `page ${p.slug}`, detail: `← ${p.file}` });
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (target && existing.collectionId !== target.id) {
|
|
100
|
+
plan.moves.push({ page: existing, to: c });
|
|
101
|
+
ctx.line({ verb: 'move', what: `page ${p.slug}`, detail: `into collection ${c.slug}` });
|
|
102
|
+
}
|
|
103
|
+
else if (!target) {
|
|
104
|
+
plan.moves.push({ page: existing, to: c });
|
|
105
|
+
ctx.line({ verb: 'move', what: `page ${p.slug}`, detail: `into new collection ${c.slug}` });
|
|
106
|
+
}
|
|
107
|
+
// A folder stub never overwrites a page that already stands for the folder.
|
|
108
|
+
if (p.stub) {
|
|
109
|
+
plan.unchanged++;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const reasons = [];
|
|
113
|
+
if (ctx.force)
|
|
114
|
+
reasons.push('--force');
|
|
115
|
+
if (!existing.published)
|
|
116
|
+
reasons.push('not published');
|
|
117
|
+
if (existing.title !== p.title)
|
|
118
|
+
reasons.push(`title "${existing.title}" → "${p.title}"`);
|
|
119
|
+
if (!reasons.length) {
|
|
120
|
+
const liveBody = await bodyOf(existing);
|
|
121
|
+
if (liveBody === null)
|
|
122
|
+
reasons.push('content not readable');
|
|
123
|
+
else if (!(0, compare_1.sameText)(p.body, liveBody))
|
|
124
|
+
reasons.push('content');
|
|
125
|
+
}
|
|
126
|
+
if (reasons.length) {
|
|
127
|
+
sends.push({ page: p, why: 'update' });
|
|
128
|
+
ctx.line({
|
|
129
|
+
verb: 'update',
|
|
130
|
+
what: `page ${p.slug}`,
|
|
131
|
+
detail: `${reasons.join(', ')} ← ${p.file}`,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
else
|
|
135
|
+
plan.unchanged++;
|
|
136
|
+
}
|
|
137
|
+
if (sends.length)
|
|
138
|
+
plan.sends.set(c, sends);
|
|
139
|
+
for (const p of scoped)
|
|
140
|
+
if (p.unknown.length)
|
|
141
|
+
ctx.line({
|
|
142
|
+
verb: 'warn',
|
|
143
|
+
what: `page ${p.slug}`,
|
|
144
|
+
detail: `front matter not understood, not sent: ${p.unknown.join(', ')}`,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
if (ctx.prune)
|
|
148
|
+
planPrune(ctx, live, plan, openapiCollections);
|
|
149
|
+
return plan;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* `--prune`: what the folder no longer has.
|
|
153
|
+
*
|
|
154
|
+
* Never on a narrowed run, never a collection an OpenAPI import owns, never a
|
|
155
|
+
* slug or collection the manifest says to `keep`, and never a page with a kept
|
|
156
|
+
* page underneath it — deleting a parent takes its children to the trash with
|
|
157
|
+
* it, and "I removed one file" must not cost a subtree. Pages go to the trash,
|
|
158
|
+
* where they can be restored.
|
|
159
|
+
*/
|
|
160
|
+
function planPrune(ctx, live, plan, openapiCollections) {
|
|
161
|
+
if (!ctx.only.all) {
|
|
162
|
+
ctx.line({
|
|
163
|
+
verb: 'skip',
|
|
164
|
+
what: 'prune',
|
|
165
|
+
detail: 'not with --only — a narrowed run never deletes',
|
|
166
|
+
});
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const keep = new Set(ctx.spec.keep ?? []);
|
|
170
|
+
const wanted = new Set(ctx.scan.collections.flatMap((c) => c.pages.map((p) => p.slug)));
|
|
171
|
+
const managed = new Set([...plan.collectionOf.values()].filter(Boolean).map((c) => c.id));
|
|
172
|
+
const protectedCols = new Set(live.collections
|
|
173
|
+
.filter((c) => openapiCollections.has(c.name.toLowerCase()) ||
|
|
174
|
+
openapiCollections.has(c.slug) ||
|
|
175
|
+
keep.has(c.slug) ||
|
|
176
|
+
keep.has(c.name))
|
|
177
|
+
.map((c) => c.id));
|
|
178
|
+
const doomed = new Set(live.pages
|
|
179
|
+
.filter((p) => !wanted.has(p.slug) && !keep.has(p.slug) && !protectedCols.has(p.collectionId))
|
|
180
|
+
.map((p) => p.id));
|
|
181
|
+
const children = new Map();
|
|
182
|
+
for (const p of live.pages)
|
|
183
|
+
if (p.parentId)
|
|
184
|
+
children.set(p.parentId, [...(children.get(p.parentId) ?? []), p]);
|
|
185
|
+
const keepsSomething = (id) => (children.get(id) ?? []).some((c) => !doomed.has(c.id) || keepsSomething(c.id));
|
|
186
|
+
for (const p of live.pages) {
|
|
187
|
+
if (!doomed.has(p.id))
|
|
188
|
+
continue;
|
|
189
|
+
if (keepsSomething(p.id)) {
|
|
190
|
+
ctx.line({
|
|
191
|
+
verb: 'skip',
|
|
192
|
+
what: `prune ${p.slug}`,
|
|
193
|
+
detail: 'has pages under it that are kept',
|
|
194
|
+
});
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
plan.prunePages.push(p);
|
|
198
|
+
ctx.line({ verb: 'prune', what: `page ${p.slug}`, detail: 'no file — to the trash' });
|
|
199
|
+
}
|
|
200
|
+
const prunedIds = new Set(plan.prunePages.map((p) => p.id));
|
|
201
|
+
for (const c of live.collections) {
|
|
202
|
+
if (managed.has(c.id) || protectedCols.has(c.id) || c.isDefault)
|
|
203
|
+
continue;
|
|
204
|
+
const rest = live.pages.filter((p) => p.collectionId === c.id && !prunedIds.has(p.id));
|
|
205
|
+
if (rest.length)
|
|
206
|
+
continue;
|
|
207
|
+
plan.pruneCollections.push(c);
|
|
208
|
+
ctx.line({ verb: 'prune', what: `collection ${c.slug}`, detail: 'no folder' });
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=docs-plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs-plan.js","sourceRoot":"","sources":["../../src/publish/docs-plan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type SpaceCtx } from './context';
|
|
2
|
+
import type { DocsLive } from './docs-plan';
|
|
3
|
+
import type { LiveCollection, LocalCollection } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* After the bodies land: metadata, nesting and order.
|
|
6
|
+
*
|
|
7
|
+
* Run twice in a real publish — never, in fact, on a guess. On a dry run it
|
|
8
|
+
* reads the space as it is and says what would change; on a real run it reads
|
|
9
|
+
* the space AGAIN after the import, because the pages this run created only
|
|
10
|
+
* have ids now, and acts on that. One function, so the plan and the run cannot
|
|
11
|
+
* disagree about what "in order" means.
|
|
12
|
+
*/
|
|
13
|
+
export interface ReconcileInput {
|
|
14
|
+
live: DocsLive;
|
|
15
|
+
collectionOf: Map<LocalCollection, LiveCollection | undefined>;
|
|
16
|
+
/** Current metadata of a page, or null when it cannot be read (no token). */
|
|
17
|
+
metaOf: (pageId: string) => Promise<Record<string, unknown> | null>;
|
|
18
|
+
/** Per file: local reference → asset URL, for metadata that names a local image. */
|
|
19
|
+
urlsFor: (file: string) => Map<string, string>;
|
|
20
|
+
/** Shared across a run: whether `POST …/collections/{id}/order` exists. */
|
|
21
|
+
bulkOrder: {
|
|
22
|
+
available: boolean | undefined;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export declare function reconcileDocs(ctx: SpaceCtx, input: ReconcileInput, apply: boolean): Promise<void>;
|
|
26
|
+
//# sourceMappingURL=docs-reconcile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs-reconcile.d.ts","sourceRoot":"","sources":["../../src/publish/docs-reconcile.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAa,MAAM,SAAS,CAAC;AAE1E;;;;;;;;GAQG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,QAAQ,CAAC;IACf,YAAY,EAAE,GAAG,CAAC,eAAe,EAAE,cAAc,GAAG,SAAS,CAAC,CAAC;IAC/D,6EAA6E;IAC7E,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IACpE,oFAAoF;IACpF,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/C,2EAA2E;IAC3E,SAAS,EAAE;QAAE,SAAS,EAAE,OAAO,GAAG,SAAS,CAAA;KAAE,CAAC;CAC/C;AAUD,wBAAsB,aAAa,CACjC,GAAG,EAAE,QAAQ,EACb,KAAK,EAAE,cAAc,EACrB,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,IAAI,CAAC,CAgHf"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.reconcileDocs = reconcileDocs;
|
|
4
|
+
const compare_1 = require("./compare");
|
|
5
|
+
const context_1 = require("./context");
|
|
6
|
+
const rewrite_1 = require("./rewrite");
|
|
7
|
+
/** Keys a public page row carries, under the metadata name they compare with. */
|
|
8
|
+
const PUBLIC_META = {
|
|
9
|
+
icon: 'icon',
|
|
10
|
+
tags: 'tags',
|
|
11
|
+
hideFromNav: 'hideFromNav',
|
|
12
|
+
seoDescription: 'description',
|
|
13
|
+
};
|
|
14
|
+
async function reconcileDocs(ctx, input, apply) {
|
|
15
|
+
const { live } = input;
|
|
16
|
+
const bySlug = new Map(live.pages.map((p) => [p.slug, p]));
|
|
17
|
+
const idOf = (slug) => bySlug.get(slug)?.id ?? `new:${slug}`;
|
|
18
|
+
const scoped = (c, p) => ctx.only.piece(ctx.spec, p.slug, p.file, c);
|
|
19
|
+
// ── Metadata ──────────────────────────────────────────────────────────────
|
|
20
|
+
for (const c of ctx.scan.collections) {
|
|
21
|
+
for (const p of c.pages) {
|
|
22
|
+
if (!scoped(c, p) || !Object.keys(p.meta).length)
|
|
23
|
+
continue;
|
|
24
|
+
const want = (0, rewrite_1.rewriteValue)(p.meta, input.urlsFor(p.file));
|
|
25
|
+
const page = bySlug.get(p.slug);
|
|
26
|
+
if (!page) {
|
|
27
|
+
if (apply)
|
|
28
|
+
ctx.errors.push(`page ${p.slug} is missing after the import — metadata not set`);
|
|
29
|
+
else
|
|
30
|
+
ctx.line({
|
|
31
|
+
verb: 'meta',
|
|
32
|
+
what: `page ${p.slug}`,
|
|
33
|
+
detail: `set ${Object.keys(want).join(', ')} (new page)`,
|
|
34
|
+
});
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
let current = await input.metaOf(page.id);
|
|
38
|
+
let unknown = [];
|
|
39
|
+
if (current === null) {
|
|
40
|
+
// Anonymous: the public row has a few of these; the rest cannot be compared.
|
|
41
|
+
const row = live.publicRows.get(page.id) ?? {};
|
|
42
|
+
current = {};
|
|
43
|
+
for (const k of Object.keys(want)) {
|
|
44
|
+
if (PUBLIC_META[k])
|
|
45
|
+
current[k] = row[PUBLIC_META[k]] ?? null;
|
|
46
|
+
else
|
|
47
|
+
unknown.push(k);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const keys = (0, compare_1.differingKeys)(current, want).filter((k) => !unknown.includes(k));
|
|
51
|
+
unknown = unknown.filter((k) => want[k] !== undefined);
|
|
52
|
+
if (unknown.length && !apply)
|
|
53
|
+
ctx.line({
|
|
54
|
+
verb: 'meta',
|
|
55
|
+
what: `page ${p.slug}`,
|
|
56
|
+
detail: `cannot compare without a token: ${unknown.join(', ')}`,
|
|
57
|
+
});
|
|
58
|
+
if (!keys.length)
|
|
59
|
+
continue;
|
|
60
|
+
if (!apply) {
|
|
61
|
+
ctx.line({ verb: 'meta', what: `page ${p.slug}`, detail: keys.join(', ') });
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
await ctx.api.patch((0, context_1.spacePath)(ctx, `/pages/${page.id}/metadata`), Object.fromEntries(keys.map((k) => [k, want[k]])));
|
|
65
|
+
ctx.line({ verb: 'meta', what: `page ${p.slug}`, detail: keys.join(', ') });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// ── Nesting and order, one sibling group at a time ────────────────────────
|
|
69
|
+
for (const c of ctx.scan.collections) {
|
|
70
|
+
const col = input.collectionOf.get(c);
|
|
71
|
+
const cid = col?.id ?? `new:${c.slug}`;
|
|
72
|
+
const groups = new Map();
|
|
73
|
+
for (const p of c.pages)
|
|
74
|
+
groups.set(p.parentSlug, [...(groups.get(p.parentSlug) ?? []), p]);
|
|
75
|
+
for (const [parentSlug, pages] of groups) {
|
|
76
|
+
// A narrowed run orders a group only if it may touch every page in it.
|
|
77
|
+
if (!pages.every((p) => scoped(c, p)))
|
|
78
|
+
continue;
|
|
79
|
+
const pid = parentSlug === null ? null : idOf(parentSlug);
|
|
80
|
+
const desired = [...pages]
|
|
81
|
+
.sort((a, b) => a.order[0] - b.order[0] || a.order[1].localeCompare(b.order[1]))
|
|
82
|
+
.map((p) => idOf(p.slug));
|
|
83
|
+
// `GET …/pages` arrives in tree order; position in that list IS the order.
|
|
84
|
+
const current = live.pages
|
|
85
|
+
.filter((p) => p.collectionId === cid && p.parentId === pid)
|
|
86
|
+
.map((p) => p.id);
|
|
87
|
+
const placed = new Set(current);
|
|
88
|
+
const keep = (0, compare_1.stayPut)(desired.filter((id) => placed.has(id)), current);
|
|
89
|
+
const moving = desired.filter((id) => !keep.has(id));
|
|
90
|
+
if (!moving.length)
|
|
91
|
+
continue;
|
|
92
|
+
const where = `${c.slug}${parentSlug ? `/${parentSlug}` : ''}`;
|
|
93
|
+
const names = moving.map((id) => live.pages.find((p) => p.id === id)?.slug ?? id.replace(/^new:/, ''));
|
|
94
|
+
ctx.line({
|
|
95
|
+
verb: 'order',
|
|
96
|
+
what: where,
|
|
97
|
+
detail: `${moving.length} to place: ${names.slice(0, 8).join(', ')}${names.length > 8 ? ', …' : ''}`,
|
|
98
|
+
});
|
|
99
|
+
if (!apply)
|
|
100
|
+
continue;
|
|
101
|
+
if (pid?.startsWith('new:') || cid.startsWith('new:')) {
|
|
102
|
+
ctx.errors.push(`${where}: parent or collection missing after the import — order not set`);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
await placeGroup(ctx, cid, pid, desired, moving, current, input.bulkOrder);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// ── Collection order ───────────────────────────────────────────────────────
|
|
109
|
+
if (!ctx.only.all)
|
|
110
|
+
return;
|
|
111
|
+
const desiredCols = ctx.scan.collections.map((c) => input.collectionOf.get(c)?.id ?? `new:${c.slug}`);
|
|
112
|
+
const currentCols = live.collections.map((c) => c.id).filter((id) => desiredCols.includes(id));
|
|
113
|
+
if (desiredCols.join() === currentCols.join())
|
|
114
|
+
return;
|
|
115
|
+
ctx.line({
|
|
116
|
+
verb: 'order',
|
|
117
|
+
what: 'collections',
|
|
118
|
+
detail: ctx.scan.collections.map((c) => c.slug).join(', '),
|
|
119
|
+
});
|
|
120
|
+
if (apply)
|
|
121
|
+
await ctx.api.post((0, context_1.spacePath)(ctx, '/collections/reorder'), {
|
|
122
|
+
ids: desiredCols.filter((id) => !id.startsWith('new:')),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Put one sibling group in order.
|
|
127
|
+
*
|
|
128
|
+
* `POST …/collections/{id}/order` does it in one call when the API
|
|
129
|
+
* has it; until then — and for any group it refuses — one paced
|
|
130
|
+
* `POST …/pages/{id}/reorder` per page that is out of place, each placed after
|
|
131
|
+
* its predecessor in the desired order. A page under the wrong parent is
|
|
132
|
+
* always moved one at a time, because that call is the one that re-parents.
|
|
133
|
+
*/
|
|
134
|
+
async function placeGroup(ctx, cid, pid, desired, moving, current, bulk) {
|
|
135
|
+
const reparent = moving.some((id) => !current.includes(id));
|
|
136
|
+
if (!reparent && bulk.available !== false) {
|
|
137
|
+
// Unmanaged siblings keep their relative order, after the managed ones.
|
|
138
|
+
const pageIds = [...desired, ...current.filter((id) => !desired.includes(id))];
|
|
139
|
+
try {
|
|
140
|
+
await ctx.api.post((0, context_1.spacePath)(ctx, `/collections/${cid}/order`), pid ? { pageIds, parentId: pid } : { pageIds });
|
|
141
|
+
bulk.available = true;
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
catch (e) {
|
|
145
|
+
const status = e.status;
|
|
146
|
+
// 404/405: the route is not there (yet) — stop asking for the rest of the run.
|
|
147
|
+
if (status === 404 || status === 405)
|
|
148
|
+
bulk.available = false;
|
|
149
|
+
else if (status !== 400 && status !== 422)
|
|
150
|
+
throw e;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const keep = new Set(desired.filter((id) => !moving.includes(id)));
|
|
154
|
+
let prev = null;
|
|
155
|
+
for (const id of desired) {
|
|
156
|
+
if (!keep.has(id)) {
|
|
157
|
+
await ctx.api.post((0, context_1.spacePath)(ctx, `/pages/${id}/reorder`), { parentId: pid, afterId: prev });
|
|
158
|
+
}
|
|
159
|
+
prev = id;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=docs-reconcile.js.map
|