@canopy-iiif/app 0.8.5 → 0.9.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/AGENTS.md CHANGED
@@ -34,6 +34,11 @@ Current Focus
34
34
  - Runtime bundling: review `search/search.js` for esbuild config drift (externals, shims); list required adjustments if UI adds new hydrated components.
35
35
  - Cache hygiene: document when `.cache/mdx` vs `.cache/iiif` are pruned and add TODOs if cleanup relies on manual intervention.
36
36
 
37
+ ### Interstitial hero support
38
+ - Featured manifests come from `canopy.yml → featured`; `lib/components/featured.js` normalizes IDs, resolves slugs from `.cache/iiif/index.json`, and surfaces thumbnails (`heroThumbnail*` when the IIIF build computed representative images).
39
+ - `build/iiif.js` now writes `heroThumbnail`, `heroThumbnailWidth`, and `heroThumbnailHeight` alongside standard thumbnails so the UI hero can display consistent crops.
40
+ - `build/mdx.js` → `ensureHeroRuntime()` bundles `packages/app/lib/components/hero-slider-runtime.js` to `site/scripts/canopy-hero-slider.js`; keep Swiper external and mirror any esbuild option changes in the UI workspace docs.
41
+
37
42
  Risks & Watchpoints
38
43
  -------------------
39
44
  - Long reruns: if `dev` mode clears `.cache/iiif` too aggressively it slows iteration; capture triggers before modifying cleanup behavior.
@@ -19,7 +19,6 @@ const {
19
19
  const { ensureStyles } = require("./styles");
20
20
  const { copyAssets } = require("./assets");
21
21
  const { logLine } = require("./log");
22
- const { verifyBuildOutput } = require("./verify");
23
22
  const navigation = require("../components/navigation");
24
23
 
25
24
  // hold records between builds if skipping IIIF
@@ -74,7 +73,7 @@ async function build(options = {}) {
74
73
  );
75
74
  }
76
75
  // Ensure any configured featured manifests are cached (and thumbnails computed)
77
- // so SSR components like <Hero /> can resolve items even if they are not part of
76
+ // so SSR interstitials can resolve items even if they are not part of
78
77
  // the traversed collection or when IIIF build is skipped during incremental rebuilds.
79
78
  try { await iiif.ensureFeaturedInCache(CONFIG); } catch (_) {}
80
79
 
@@ -87,7 +86,7 @@ async function build(options = {}) {
87
86
  bright: true,
88
87
  underscore: true,
89
88
  });
90
- // FeaturedHero now reads directly from the local IIIF cache; no API file needed
89
+ // Interstitials read directly from the local IIIF cache; no API file needed
91
90
  pageRecords = await searchBuild.collectMdxPageRecords();
92
91
  await pages.buildContentTree(CONTENT_DIR, pageRecords);
93
92
  logLine("✓ MDX pages built", "green");
@@ -135,16 +134,6 @@ async function build(options = {}) {
135
134
  });
136
135
  await copyAssets();
137
136
 
138
- /**
139
- * Final verification (checklist)
140
- */
141
- try {
142
- verifyBuildOutput({ outDir: OUT_DIR });
143
- } catch (e) {
144
- logLine("✗ Build verification failed", "red", { bright: true });
145
- logLine(String(e && e.message ? e.message : e), "red");
146
- process.exit(1);
147
- }
148
137
  }
149
138
 
150
139
  module.exports = { build };
package/lib/build/dev.js CHANGED
@@ -1050,6 +1050,13 @@ async function dev() {
1050
1050
  }
1051
1051
  };
1052
1052
  attachCssWatcher();
1053
+ const handleUiSassChange = () => {
1054
+ attachCssWatcher();
1055
+ scheduleTailwindRestart(
1056
+ "[tailwind] detected @canopy-iiif/app/ui Sass change — restarting Tailwind",
1057
+ "[tailwind] compile after UI Sass change failed"
1058
+ );
1059
+ };
1053
1060
  if (fs.existsSync(uiStylesDir)) {
1054
1061
  try {
1055
1062
  fs.watch(
@@ -1057,7 +1064,7 @@ async function dev() {
1057
1064
  { persistent: false, recursive: true },
1058
1065
  (evt, fn) => {
1059
1066
  try {
1060
- if (fn && /\.s[ac]ss$/i.test(String(fn))) attachCssWatcher();
1067
+ if (fn && /\.s[ac]ss$/i.test(String(fn))) handleUiSassChange();
1061
1068
  } catch (_) {}
1062
1069
  }
1063
1070
  );
@@ -1071,7 +1078,7 @@ async function dev() {
1071
1078
  { persistent: false },
1072
1079
  (evt, fn) => {
1073
1080
  try {
1074
- if (fn && /\.s[ac]ss$/i.test(String(fn))) attachCssWatcher();
1081
+ if (fn && /\.s[ac]ss$/i.test(String(fn))) handleUiSassChange();
1075
1082
  } catch (_) {}
1076
1083
  scan(dir);
1077
1084
  }
package/lib/build/iiif.js CHANGED
@@ -578,7 +578,7 @@ async function saveCachedManifest(manifest, id, parentId) {
578
578
  }
579
579
 
580
580
  // Ensure any configured featured manifests are present in the local cache
581
- // (and have thumbnails computed) so SSR components like <Hero /> can read them.
581
+ // (and have thumbnails computed) so interstitial heroes can read them.
582
582
  async function ensureFeaturedInCache(cfg) {
583
583
  try {
584
584
  const CONFIG = cfg || (await loadConfig());
@@ -1144,9 +1144,10 @@ async function buildIiifCollectionPages(CONFIG) {
1144
1144
  React.createElement(app.Head)
1145
1145
  )
1146
1146
  : "";
1147
- const needsHydrateViewer = body.includes("data-canopy-viewer");
1147
+ const needsHydrateViewer =
1148
+ body.includes("data-canopy-viewer") || body.includes("data-canopy-scroll");
1148
1149
  const needsRelated = body.includes("data-canopy-related-items");
1149
- const needsHero = body.includes("data-canopy-hero");
1150
+ const needsHeroSlider = body.includes("data-canopy-hero-slider");
1150
1151
  const needsSearchForm = body.includes("data-canopy-search-form");
1151
1152
  const needsHydrate =
1152
1153
  body.includes("data-canopy-hydrate") ||
@@ -1172,11 +1173,11 @@ async function buildIiifCollectionPages(CONFIG) {
1172
1173
  .split(path.sep)
1173
1174
  .join("/")
1174
1175
  : null;
1175
- const heroRel = needsHero
1176
+ const heroRel = needsHeroSlider
1176
1177
  ? path
1177
1178
  .relative(
1178
1179
  path.dirname(outPath),
1179
- path.join(OUT_DIR, "scripts", "canopy-hero.js")
1180
+ path.join(OUT_DIR, "scripts", "canopy-hero-slider.js")
1180
1181
  )
1181
1182
  .split(path.sep)
1182
1183
  .join("/")
@@ -1201,15 +1202,14 @@ async function buildIiifCollectionPages(CONFIG) {
1201
1202
  : null;
1202
1203
 
1203
1204
  let jsRel = null;
1204
- if (needsHero && heroRel) jsRel = heroRel;
1205
+ if (needsHeroSlider && heroRel) jsRel = heroRel;
1205
1206
  else if (needsRelated && sliderRel) jsRel = sliderRel;
1206
1207
  else if (viewerRel) jsRel = viewerRel;
1207
1208
 
1208
1209
  let headExtra = head;
1209
1210
  const needsReact = !!(
1210
1211
  needsHydrateViewer ||
1211
- needsRelated ||
1212
- needsHero
1212
+ needsRelated
1213
1213
  );
1214
1214
  let vendorTag = "";
1215
1215
  if (needsReact) {
package/lib/build/mdx.js CHANGED
@@ -113,7 +113,7 @@ async function loadUiComponents() {
113
113
  }
114
114
  let comp = (mod && typeof mod === 'object') ? mod : {};
115
115
  // Hard-require core exports; do not inject fallbacks
116
- const required = ['SearchPanel', 'SearchFormModal', 'SearchResults', 'SearchSummary', 'SearchTabs', 'Viewer', 'Slider', 'RelatedItems', 'Hero', 'FeaturedHero'];
116
+ const required = ['SearchPanel', 'SearchFormModal', 'SearchResults', 'SearchSummary', 'SearchTabs', 'Viewer', 'Slider', 'RelatedItems', 'Interstitials'];
117
117
  const missing = required.filter((k) => !comp || !comp[k]);
118
118
  if (missing.length) {
119
119
  throw new Error('[canopy][mdx] Missing UI exports: ' + missing.join(', '));
@@ -358,6 +358,7 @@ async function ensureClientRuntime() {
358
358
  const outFile = path.join(scriptsDir, "canopy-viewer.js");
359
359
  const entry = `
360
360
  import CloverViewer from '@samvera/clover-iiif/viewer';
361
+ import CloverScroll from '@samvera/clover-iiif/scroll';
361
362
 
362
363
  function ready(fn) {
363
364
  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', fn, { once: true });
@@ -373,22 +374,29 @@ async function ensureClientRuntime() {
373
374
  } catch (_) { return {}; }
374
375
  }
375
376
 
376
- ready(function() {
377
+ function mountAll(selector, Component) {
377
378
  try {
378
- const nodes = document.querySelectorAll('[data-canopy-viewer]');
379
- if (!nodes || !nodes.length) return;
379
+ const nodes = document.querySelectorAll(selector);
380
+ if (!nodes || !nodes.length || !Component) return;
381
+ const React = (window && window.React) || null;
382
+ const ReactDOMClient = (window && window.ReactDOMClient) || null;
383
+ const createRoot = ReactDOMClient && ReactDOMClient.createRoot;
384
+ if (!React || !createRoot) return;
380
385
  for (const el of nodes) {
381
386
  try {
387
+ if (el.__canopyHydrated) continue;
382
388
  const props = parseProps(el);
383
- const React = (window && window.React) || null;
384
- const ReactDOMClient = (window && window.ReactDOMClient) || null;
385
- const createRoot = ReactDOMClient && ReactDOMClient.createRoot;
386
- if (!React || !createRoot) continue;
387
389
  const root = createRoot(el);
388
- root.render(React.createElement(CloverViewer, props));
390
+ root.render(React.createElement(Component, props));
391
+ el.__canopyHydrated = true;
389
392
  } catch (_) { /* skip */ }
390
393
  }
391
394
  } catch (_) { /* no-op */ }
395
+ }
396
+
397
+ ready(function() {
398
+ mountAll('[data-canopy-viewer]', CloverViewer);
399
+ mountAll('[data-canopy-scroll]', CloverScroll);
392
400
  });
393
401
  `;
394
402
  const reactShim = `
@@ -764,67 +772,21 @@ async function ensureReactGlobals() {
764
772
  });
765
773
  }
766
774
 
767
- // Bundle a small runtime to hydrate <Hero /> placeholders from featured items
768
775
  async function ensureHeroRuntime() {
769
776
  let esbuild = null;
770
- try { esbuild = require("../../ui/node_modules/esbuild"); } catch (_) { try { esbuild = require("esbuild"); } catch (_) {} }
771
- if (!esbuild) throw new Error('Hero runtime bundling requires esbuild. Install dependencies before building.');
772
- const { path } = require("../common");
777
+ try {
778
+ esbuild = require("../ui/node_modules/esbuild");
779
+ } catch (_) {
780
+ try { esbuild = require("esbuild"); } catch (_) {}
781
+ }
782
+ if (!esbuild) throw new Error('Hero slider runtime bundling requires esbuild. Install dependencies before building.');
773
783
  ensureDirSync(OUT_DIR);
774
784
  const scriptsDir = path.join(OUT_DIR, 'scripts');
775
785
  ensureDirSync(scriptsDir);
776
- const outFile = path.join(scriptsDir, 'canopy-hero.js');
777
- const entry = `
778
- import { Hero } from '@canopy-iiif/app/ui';
779
- function ready(fn){ if(document.readyState==='loading') document.addEventListener('DOMContentLoaded', fn, { once: true }); else fn(); }
780
- function parseProps(el){ try{ const s=el.querySelector('script[type="application/json"]'); if(s) return JSON.parse(s.textContent||'{}'); }catch(_){ } return {}; }
781
- function rootBase(){ try { var bp=(window&&window.CANOPY_BASE_PATH)?String(window.CANOPY_BASE_PATH):''; return bp && bp.endsWith('/') ? bp.slice(0,-1) : bp; } catch(_){ return ''; } }
782
- async function getApiVersion(){ try{ const u=rootBase() + '/api/index.json'; const res=await fetch(u).catch(()=>null); const j=res&&res.ok?await res.json().catch(()=>null):null; return (j && j.version) || ''; }catch(_){ return ''; } }
783
- async function loadFeatured(){ try { const v = await getApiVersion(); const q = v ? ('?v='+encodeURIComponent(v)) : ''; const res = await fetch(rootBase() + '/api/featured.json' + q).catch(()=>null); const j = res && res.ok ? await res.json().catch(()=>[]) : []; return Array.isArray(j) ? j : (j && j.items) || []; } catch(_){ return []; } }
784
- function mount(el, rec){ try{ const React=(window&&window.React)||null; const RDC=(window&&window.ReactDOMClient)||null; const createRoot = RDC && RDC.createRoot; if(!React||!createRoot) return; const props = parseProps(el) || {}; const height = props.height || 360; const node = React.createElement(Hero, { height, item: rec }); const root=createRoot(el); root.render(node); } catch(_){} }
785
- ready(async function(){ const hosts = Array.from(document.querySelectorAll('[data-canopy-hero]')); if(!hosts.length) return; const featured = await loadFeatured(); if(!featured.length) return; hosts.forEach((el, i) => { try { const p = parseProps(el) || {}; let idx = 0; if (p && typeof p.index === 'number') idx = Math.max(0, Math.min(featured.length-1, Math.floor(p.index))); else if (p && (p.random===true || p.random==='true')) idx = Math.floor(Math.random() * featured.length); const rec = featured[idx] || featured[0]; if (rec) mount(el, rec); } catch(_){} }); });
786
- `;
787
- const reactShim = `
788
- const React = (typeof window !== 'undefined' && window.React) || {};
789
- export default React;
790
- export const Children = React.Children;
791
- export const Component = React.Component;
792
- export const Fragment = React.Fragment;
793
- export const createElement = React.createElement;
794
- export const cloneElement = React.cloneElement;
795
- export const createContext = React.createContext;
796
- export const forwardRef = React.forwardRef;
797
- export const memo = React.memo;
798
- export const startTransition = React.startTransition;
799
- export const isValidElement = React.isValidElement;
800
- export const useEffect = React.useEffect;
801
- export const useLayoutEffect = React.useLayoutEffect;
802
- export const useMemo = React.useMemo;
803
- export const useState = React.useState;
804
- export const useRef = React.useRef;
805
- export const useCallback = React.useCallback;
806
- export const useContext = React.useContext;
807
- export const useReducer = React.useReducer;
808
- export const useId = React.useId;
809
- `;
810
- const rdomClientShim = `
811
- const RDC = (typeof window !== 'undefined' && window.ReactDOMClient) || {};
812
- export default RDC;
813
- export const createRoot = RDC.createRoot;
814
- export const hydrateRoot = RDC.hydrateRoot;
815
- `;
816
- const plugin = {
817
- name: 'canopy-react-shims-hero',
818
- setup(build) {
819
- const ns = 'canopy-shim';
820
- build.onResolve({ filter: /^react$/ }, () => ({ path: 'react', namespace: ns }));
821
- build.onResolve({ filter: /^react-dom\/client$/ }, () => ({ path: 'react-dom-client', namespace: ns }));
822
- build.onLoad({ filter: /^react$/, namespace: ns }, () => ({ contents: reactShim, loader: 'js' }));
823
- build.onLoad({ filter: /^react-dom-client$/, namespace: ns }, () => ({ contents: rdomClientShim, loader: 'js' }));
824
- }
825
- };
786
+ const outFile = path.join(scriptsDir, 'canopy-hero-slider.js');
787
+ const entryFile = path.join(__dirname, '..', 'components', 'hero-slider-runtime.js');
826
788
  await esbuild.build({
827
- stdin: { contents: entry, resolveDir: process.cwd(), sourcefile: 'canopy-hero-entry.js', loader: 'js' },
789
+ entryPoints: [entryFile],
828
790
  outfile: outFile,
829
791
  platform: 'browser',
830
792
  format: 'iife',
@@ -833,8 +795,18 @@ async function ensureHeroRuntime() {
833
795
  target: ['es2018'],
834
796
  logLevel: 'silent',
835
797
  minify: true,
836
- plugins: [plugin],
837
798
  });
799
+ try {
800
+ const { logLine } = require('./log');
801
+ let size = 0;
802
+ try {
803
+ const st = fs.statSync(outFile);
804
+ size = (st && st.size) || 0;
805
+ } catch (_) {}
806
+ const kb = size ? ` (${(size / 1024).toFixed(1)} KB)` : '';
807
+ const rel = path.relative(process.cwd(), outFile).split(path.sep).join('/');
808
+ logLine(`✓ Wrote ${rel}${kb}`, 'cyan');
809
+ } catch (_) {}
838
810
  }
839
811
 
840
812
  module.exports = {
@@ -59,8 +59,10 @@ async function renderContentMdxToHtml(filePath, outPath, extraProps = {}) {
59
59
  mergedProps.navigation = navData;
60
60
  }
61
61
  const { body, head } = await mdx.compileMdxFile(filePath, outPath, null, mergedProps);
62
- const needsHydrateViewer = body.includes('data-canopy-viewer');
62
+ const needsHydrateViewer =
63
+ body.includes('data-canopy-viewer') || body.includes('data-canopy-scroll');
63
64
  const needsHydrateSlider = body.includes('data-canopy-slider');
65
+ const needsHeroSlider = body.includes('data-canopy-hero-slider');
64
66
  const needsSearchForm = true; // search form runtime is global
65
67
  const needsFacets = body.includes('data-canopy-related-items');
66
68
  const viewerRel = needsHydrateViewer
@@ -69,6 +71,9 @@ async function renderContentMdxToHtml(filePath, outPath, extraProps = {}) {
69
71
  const sliderRel = (needsHydrateSlider || needsFacets)
70
72
  ? path.relative(path.dirname(outPath), path.join(OUT_DIR, 'scripts', 'canopy-slider.js')).split(path.sep).join('/')
71
73
  : null;
74
+ const heroRel = needsHeroSlider
75
+ ? path.relative(path.dirname(outPath), path.join(OUT_DIR, 'scripts', 'canopy-hero-slider.js')).split(path.sep).join('/')
76
+ : null;
72
77
  const facetsRel = needsFacets
73
78
  ? path.relative(path.dirname(outPath), path.join(OUT_DIR, 'scripts', 'canopy-related-items.js')).split(path.sep).join('/')
74
79
  : null;
@@ -80,7 +85,8 @@ async function renderContentMdxToHtml(filePath, outPath, extraProps = {}) {
80
85
  searchFormRel = rel;
81
86
  }
82
87
  let jsRel = null;
83
- if (needsFacets && sliderRel) jsRel = sliderRel;
88
+ if (needsHeroSlider && heroRel) jsRel = heroRel;
89
+ else if (needsFacets && sliderRel) jsRel = sliderRel;
84
90
  else if (viewerRel) jsRel = viewerRel;
85
91
  else if (sliderRel) jsRel = sliderRel;
86
92
  else if (facetsRel) jsRel = facetsRel;
@@ -101,6 +107,7 @@ async function renderContentMdxToHtml(filePath, outPath, extraProps = {}) {
101
107
  } catch (_) {}
102
108
  let headExtra = head;
103
109
  const extraScripts = [];
110
+ if (heroRel && jsRel !== heroRel) extraScripts.push(`<script defer src="${heroRel}"></script>`);
104
111
  if (facetsRel && jsRel !== facetsRel) extraScripts.push(`<script defer src="${facetsRel}"></script>`);
105
112
  if (viewerRel && jsRel !== viewerRel) extraScripts.push(`<script defer src="${viewerRel}"></script>`);
106
113
  if (sliderRel && jsRel !== sliderRel) extraScripts.push(`<script defer src="${sliderRel}"></script>`);
@@ -5,11 +5,7 @@ async function prepareAllRuntimes() {
5
5
  const mdx = require('./mdx');
6
6
  try { await mdx.ensureClientRuntime(); } catch (_) {}
7
7
  try { if (typeof mdx.ensureSliderRuntime === 'function') await mdx.ensureSliderRuntime(); } catch (_) {}
8
- try {
9
- if (process.env.CANOPY_ENABLE_HERO_RUNTIME === '1' || process.env.CANOPY_ENABLE_HERO_RUNTIME === 'true') {
10
- if (typeof mdx.ensureHeroRuntime === 'function') await mdx.ensureHeroRuntime();
11
- }
12
- } catch (_) {}
8
+ try { if (typeof mdx.ensureHeroRuntime === 'function') await mdx.ensureHeroRuntime(); } catch (_) {}
13
9
  try { if (typeof mdx.ensureFacetsRuntime === 'function') await mdx.ensureFacetsRuntime(); } catch (_) {}
14
10
  try { if (typeof mdx.ensureReactGlobals === 'function') await mdx.ensureReactGlobals(); } catch (_) {}
15
11
  await prepareSearchFormRuntime();
@@ -0,0 +1,78 @@
1
+ import Swiper from 'swiper';
2
+ import { Navigation, Pagination, Autoplay } from 'swiper/modules';
3
+ import 'swiper/css';
4
+ import 'swiper/css/navigation';
5
+ import 'swiper/css/pagination';
6
+
7
+ function ready(fn) {
8
+ if (typeof document === 'undefined') return;
9
+ if (document.readyState === 'loading') {
10
+ document.addEventListener('DOMContentLoaded', fn, { once: true });
11
+ } else {
12
+ fn();
13
+ }
14
+ }
15
+
16
+ function initSlider(host) {
17
+ if (!host || host.__canopyHeroBound) return;
18
+ const slider = host.querySelector('.canopy-interstitial__slider');
19
+ if (!slider) return;
20
+ const prev = host.querySelector('.canopy-interstitial__nav-btn--prev');
21
+ const next = host.querySelector('.canopy-interstitial__nav-btn--next');
22
+ const pagination = host.querySelector('.canopy-interstitial__pagination');
23
+
24
+ try {
25
+ const swiperInstance = new Swiper(slider, {
26
+ modules: [Navigation, Pagination, Autoplay],
27
+ loop: true,
28
+ slidesPerView: 1,
29
+ navigation: {
30
+ prevEl: prev || undefined,
31
+ nextEl: next || undefined,
32
+ },
33
+ pagination: {
34
+ el: pagination || undefined,
35
+ clickable: true,
36
+ },
37
+ autoplay: {
38
+ delay: 6000,
39
+ disableOnInteraction: false,
40
+ },
41
+ });
42
+ host.__canopyHeroBound = true;
43
+ host.__canopyHeroSwiper = swiperInstance;
44
+ } catch (error) {
45
+ try {
46
+ console.warn('[canopy][hero] failed to initialise slider', error);
47
+ } catch (_) {}
48
+ }
49
+ }
50
+
51
+ function observeHosts() {
52
+ try {
53
+ const observer = new MutationObserver((mutations) => {
54
+ mutations.forEach((mutation) => {
55
+ mutation.addedNodes &&
56
+ mutation.addedNodes.forEach((node) => {
57
+ if (!(node instanceof Element)) return;
58
+ if (node.matches && node.matches('[data-canopy-hero-slider]')) initSlider(node);
59
+ const inner = node.querySelectorAll
60
+ ? node.querySelectorAll('[data-canopy-hero-slider]')
61
+ : [];
62
+ inner && inner.forEach && inner.forEach((el) => initSlider(el));
63
+ });
64
+ });
65
+ });
66
+ observer.observe(document.documentElement || document.body, {
67
+ childList: true,
68
+ subtree: true,
69
+ });
70
+ } catch (_) {}
71
+ }
72
+
73
+ ready(() => {
74
+ if (typeof document === 'undefined') return;
75
+ const hosts = document.querySelectorAll('[data-canopy-hero-slider]');
76
+ hosts.forEach((host) => initSlider(host));
77
+ observeHosts();
78
+ });
@@ -128,26 +128,6 @@ function attachSignalHandlers() {
128
128
  process.on('exit', clean);
129
129
  }
130
130
 
131
- function verifyBuildOutput(outDir = 'site') {
132
- const root = path.resolve(outDir);
133
- function walk(dir) {
134
- let count = 0;
135
- if (!fs.existsSync(dir)) return 0;
136
- const entries = fs.readdirSync(dir, { withFileTypes: true });
137
- for (const entry of entries) {
138
- const p = path.join(dir, entry.name);
139
- if (entry.isDirectory()) count += walk(p);
140
- else if (entry.isFile() && p.toLowerCase().endsWith('.html')) count += 1;
141
- }
142
- return count;
143
- }
144
- const pages = walk(root);
145
- if (!pages) {
146
- throw new Error('CI check failed: no HTML pages generated in "site/".');
147
- }
148
- log(`CI check: found ${pages} HTML page(s) in ${root}.`);
149
- }
150
-
151
131
  async function orchestrate(options = {}) {
152
132
  const argv = options.argv || process.argv.slice(2);
153
133
  const env = options.env || process.env;
@@ -156,12 +136,6 @@ async function orchestrate(options = {}) {
156
136
  const mode = getMode(argv, env);
157
137
  log(`Mode: ${mode}`);
158
138
 
159
- const cli = new Set(argv);
160
- if (cli.has('--verify')) {
161
- verifyBuildOutput(env.CANOPY_OUT_DIR || 'site');
162
- return;
163
- }
164
-
165
139
  await prepareUi(mode, env);
166
140
 
167
141
  const api = loadLibraryApi();
@@ -176,9 +150,6 @@ async function orchestrate(options = {}) {
176
150
  await api.build();
177
151
  }
178
152
  log('Build complete');
179
- if (env.CANOPY_VERIFY === '1' || env.CANOPY_VERIFY === 'true') {
180
- verifyBuildOutput(env.CANOPY_OUT_DIR || 'site');
181
- }
182
153
  }
183
154
  } finally {
184
155
  if (uiWatcherChild && !uiWatcherChild.killed) {
@@ -189,7 +160,6 @@ async function orchestrate(options = {}) {
189
160
 
190
161
  module.exports = {
191
162
  orchestrate,
192
- verifyBuildOutput,
193
163
  _internals: {
194
164
  getMode,
195
165
  prepareUi,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canopy-iiif/app",
3
- "version": "0.8.5",
3
+ "version": "0.9.0",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "author": "Mat Jordan <mat@northwestern.edu>",
@@ -15,4 +15,3 @@ export interface OrchestratorOptions {
15
15
  }
16
16
 
17
17
  export declare function orchestrate(options?: OrchestratorOptions): Promise<void>;
18
- export declare function verifyBuildOutput(outDir?: string): void;