@canopy-iiif/app 1.5.16 → 1.6.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/lib/build/iiif.js +30 -1
- package/lib/common.js +32 -0
- package/lib/search/search.js +15 -3
- package/package.json +1 -1
package/lib/build/iiif.js
CHANGED
|
@@ -99,6 +99,20 @@ function normalizeCollectionUris(value) {
|
|
|
99
99
|
return uris;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
function normalizeManifestConfig(cfg) {
|
|
103
|
+
if (!cfg || typeof cfg !== "object") return [];
|
|
104
|
+
const entries = [];
|
|
105
|
+
const push = (value) => {
|
|
106
|
+
if (value === undefined || value === null) return;
|
|
107
|
+
if (Array.isArray(value)) entries.push(...value);
|
|
108
|
+
else entries.push(value);
|
|
109
|
+
};
|
|
110
|
+
push(cfg.manifest);
|
|
111
|
+
push(cfg.manifests);
|
|
112
|
+
if (!entries.length) return [];
|
|
113
|
+
return normalizeCollectionUris(entries);
|
|
114
|
+
}
|
|
115
|
+
|
|
102
116
|
function clampSlugLength(slug, limit = MAX_ENTRY_SLUG_LENGTH) {
|
|
103
117
|
if (!slug) return "";
|
|
104
118
|
const max = Math.max(1, limit);
|
|
@@ -1457,7 +1471,8 @@ async function buildIiifCollectionPages(CONFIG) {
|
|
|
1457
1471
|
process.env.CANOPY_COLLECTION_URI || ""
|
|
1458
1472
|
);
|
|
1459
1473
|
}
|
|
1460
|
-
|
|
1474
|
+
const manifestUris = normalizeManifestConfig(cfg);
|
|
1475
|
+
if (!collectionUris.length && !manifestUris.length) return {searchRecords: []};
|
|
1461
1476
|
|
|
1462
1477
|
const searchIndexCfg = (cfg && cfg.search && cfg.search.index) || {};
|
|
1463
1478
|
const metadataCfg = (searchIndexCfg && searchIndexCfg.metadata) || {};
|
|
@@ -1524,6 +1539,7 @@ async function buildIiifCollectionPages(CONFIG) {
|
|
|
1524
1539
|
|
|
1525
1540
|
// Recursively traverse Collections and gather all Manifest tasks
|
|
1526
1541
|
const tasks = [];
|
|
1542
|
+
const queuedManifestIds = new Set();
|
|
1527
1543
|
const visitedCollections = new Set(); // normalized ids
|
|
1528
1544
|
const norm = (x) => {
|
|
1529
1545
|
try {
|
|
@@ -1567,7 +1583,11 @@ async function buildIiifCollectionPages(CONFIG) {
|
|
|
1567
1583
|
const entryId = entry && entry.id;
|
|
1568
1584
|
if (!entryId) continue;
|
|
1569
1585
|
const entryType = normalizeIiifType(entry.type || entry.fallback || "");
|
|
1586
|
+
const dedupeKey = norm(entryId) || String(entryId || "");
|
|
1587
|
+
if (!dedupeKey) continue;
|
|
1570
1588
|
if (entryType === "manifest") {
|
|
1589
|
+
if (queuedManifestIds.has(dedupeKey)) continue;
|
|
1590
|
+
queuedManifestIds.add(dedupeKey);
|
|
1571
1591
|
tasks.push({id: entryId, parent: collectionKey});
|
|
1572
1592
|
} else if (entryType === "collection") {
|
|
1573
1593
|
await gatherFromCollection(entry.raw || entryId, collectionKey);
|
|
@@ -1597,6 +1617,14 @@ async function buildIiifCollectionPages(CONFIG) {
|
|
|
1597
1617
|
} catch (_) {}
|
|
1598
1618
|
await gatherFromCollection(normalizedRoot, "");
|
|
1599
1619
|
}
|
|
1620
|
+
if (manifestUris.length) {
|
|
1621
|
+
for (const uri of manifestUris) {
|
|
1622
|
+
const dedupeKey = norm(uri) || String(uri || "");
|
|
1623
|
+
if (!dedupeKey || queuedManifestIds.has(dedupeKey)) continue;
|
|
1624
|
+
queuedManifestIds.add(dedupeKey);
|
|
1625
|
+
tasks.push({id: uri, parent: ""});
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1600
1628
|
if (!tasks.length) return {searchRecords: []};
|
|
1601
1629
|
|
|
1602
1630
|
// Split into chunks and process with limited concurrency
|
|
@@ -2395,6 +2423,7 @@ module.exports.__TESTING__ = {
|
|
|
2395
2423
|
formatDurationMs,
|
|
2396
2424
|
resolveBoolean,
|
|
2397
2425
|
normalizeCollectionUris,
|
|
2426
|
+
normalizeManifestConfig,
|
|
2398
2427
|
clampSlugLength,
|
|
2399
2428
|
isSlugTooLong,
|
|
2400
2429
|
normalizeSlugBase,
|
package/lib/common.js
CHANGED
|
@@ -15,7 +15,10 @@ const BASE_PATH = readBasePath();
|
|
|
15
15
|
let cachedAppearance = null;
|
|
16
16
|
let cachedAccent = null;
|
|
17
17
|
let cachedSiteMetadata = null;
|
|
18
|
+
let cachedSearchPageMetadata = null;
|
|
18
19
|
const DEFAULT_SITE_TITLE = 'Site title';
|
|
20
|
+
const DEFAULT_SEARCH_PAGE_TITLE = 'Search';
|
|
21
|
+
const DEFAULT_SEARCH_PAGE_DESCRIPTION = '';
|
|
19
22
|
|
|
20
23
|
function resolveThemeAppearance() {
|
|
21
24
|
if (cachedAppearance) return cachedAppearance;
|
|
@@ -87,6 +90,32 @@ function getSiteTitle() {
|
|
|
87
90
|
return DEFAULT_SITE_TITLE;
|
|
88
91
|
}
|
|
89
92
|
|
|
93
|
+
function readSearchPageMetadata() {
|
|
94
|
+
if (cachedSearchPageMetadata) return cachedSearchPageMetadata;
|
|
95
|
+
cachedSearchPageMetadata = {
|
|
96
|
+
title: DEFAULT_SEARCH_PAGE_TITLE,
|
|
97
|
+
description: DEFAULT_SEARCH_PAGE_DESCRIPTION,
|
|
98
|
+
};
|
|
99
|
+
try {
|
|
100
|
+
const cfgPath = resolveCanopyConfigPath();
|
|
101
|
+
if (!fs.existsSync(cfgPath)) return cachedSearchPageMetadata;
|
|
102
|
+
const raw = fs.readFileSync(cfgPath, 'utf8');
|
|
103
|
+
const data = yaml.load(raw) || {};
|
|
104
|
+
const searchCfg = data && data.search ? data.search : null;
|
|
105
|
+
const pageCfg = searchCfg && searchCfg.page ? searchCfg.page : null;
|
|
106
|
+
const title = pageCfg && typeof pageCfg.title === 'string' ? pageCfg.title.trim() : '';
|
|
107
|
+
const description =
|
|
108
|
+
pageCfg && typeof pageCfg.description === 'string'
|
|
109
|
+
? pageCfg.description.trim()
|
|
110
|
+
: '';
|
|
111
|
+
cachedSearchPageMetadata = {
|
|
112
|
+
title: title || DEFAULT_SEARCH_PAGE_TITLE,
|
|
113
|
+
description: description || DEFAULT_SEARCH_PAGE_DESCRIPTION,
|
|
114
|
+
};
|
|
115
|
+
} catch (_) {}
|
|
116
|
+
return cachedSearchPageMetadata;
|
|
117
|
+
}
|
|
118
|
+
|
|
90
119
|
// Determine the absolute site origin (scheme + host[:port])
|
|
91
120
|
// Priority:
|
|
92
121
|
// 1) CANOPY_BASE_URL env
|
|
@@ -256,4 +285,7 @@ module.exports = {
|
|
|
256
285
|
readSiteMetadata,
|
|
257
286
|
getSiteTitle,
|
|
258
287
|
DEFAULT_SITE_TITLE,
|
|
288
|
+
readSearchPageMetadata,
|
|
289
|
+
DEFAULT_SEARCH_PAGE_TITLE,
|
|
290
|
+
DEFAULT_SEARCH_PAGE_DESCRIPTION,
|
|
259
291
|
};
|
package/lib/search/search.js
CHANGED
|
@@ -11,6 +11,7 @@ const {
|
|
|
11
11
|
OUT_DIR,
|
|
12
12
|
htmlShell,
|
|
13
13
|
canopyBodyClassForType,
|
|
14
|
+
readSearchPageMetadata,
|
|
14
15
|
} = require('../common');
|
|
15
16
|
const { resolveCanopyConfigPath } = require('../config-path');
|
|
16
17
|
|
|
@@ -259,14 +260,25 @@ async function buildSearchPage() {
|
|
|
259
260
|
}
|
|
260
261
|
const mdx = require('../build/mdx');
|
|
261
262
|
const searchHref = rootRelativeHref('search.html');
|
|
263
|
+
const searchPageMeta = readSearchPageMetadata() || {};
|
|
264
|
+
const pageTitle =
|
|
265
|
+
typeof searchPageMeta.title === 'string' && searchPageMeta.title.trim()
|
|
266
|
+
? searchPageMeta.title.trim()
|
|
267
|
+
: 'Search';
|
|
268
|
+
const pageDescription =
|
|
269
|
+
typeof searchPageMeta.description === 'string'
|
|
270
|
+
? searchPageMeta.description
|
|
271
|
+
: '';
|
|
262
272
|
const pageDetails = {
|
|
263
|
-
title:
|
|
273
|
+
title: pageTitle,
|
|
274
|
+
description: pageDescription,
|
|
264
275
|
href: searchHref,
|
|
265
276
|
url: searchHref,
|
|
266
277
|
type: 'search',
|
|
267
278
|
canonical: searchHref,
|
|
268
279
|
meta: {
|
|
269
|
-
title:
|
|
280
|
+
title: pageTitle,
|
|
281
|
+
description: pageDescription,
|
|
270
282
|
type: 'search',
|
|
271
283
|
url: searchHref,
|
|
272
284
|
canonical: searchHref,
|
|
@@ -312,7 +324,7 @@ async function buildSearchPage() {
|
|
|
312
324
|
}
|
|
313
325
|
} catch (_) {}
|
|
314
326
|
const bodyClass = canopyBodyClassForType('search');
|
|
315
|
-
let html = htmlShell({ title:
|
|
327
|
+
let html = htmlShell({ title: pageTitle, body, cssHref: null, scriptHref: jsRel, headExtra, bodyClass });
|
|
316
328
|
try { html = require('../common').applyBaseToHtml(html); } catch (_) {}
|
|
317
329
|
await fsp.writeFile(outPath, html, 'utf8');
|
|
318
330
|
console.log('Search: Built', path.relative(process.cwd(), outPath));
|