@inizioevoke/veeva-astroclm-core 1.3.9 → 1.4.1

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.
@@ -44,6 +44,7 @@ export default function ({ veevaEnv = 'client', isTraining = false }: IVeevaConf
44
44
 
45
45
  shared: {
46
46
  externalId: formatExtId('SHARED'),
47
+ // zipFileName: 'shared.zip',
47
48
  ...binderFields
48
49
  },
49
50
 
package/dist/lib/utils.js CHANGED
@@ -2,7 +2,7 @@ export function formatExtId(...ids) {
2
2
  return ids.map((id) => { return id.toUpperCase(); }).join('_');
3
3
  }
4
4
  export function pathToSlideZip(path) {
5
- return `${flattenPath(path, { slashes: false })}.zip`;
5
+ return `${flattenPath(path, { slashes: false }).replace(/\.zip$/i, '')}.zip`;
6
6
  }
7
7
  export function flattenPath(path, { slashes = true } = {}) {
8
8
  return `${slashes ? '/' : ''}${path.toLowerCase().split('/').filter(s => s !== '').join('-')}${slashes ? '/' : ''}`;
@@ -152,7 +152,7 @@ function createSlideRow(headers, config, slide, additional) {
152
152
  addColumn(headers, row, HEADER_SLIDE_REACTIONS, disable.has('Reactions') ? ',a,b,c' : '');
153
153
  disable.delete('Reactions');
154
154
  addColumn(headers, row, HEADER_SLIDE_DISABLE_ACTIONS, [...disable].join(','));
155
- addColumn(headers, row, HEADER_SLIDE_FILE_NAME, pathToSlideZip(slide.path));
155
+ addColumn(headers, row, HEADER_SLIDE_FILE_NAME, pathToSlideZip(slide.zipFileName ?? slide.path));
156
156
  addColumn(headers, row, HEADER_SLIDE_RELATED_SHARED, config.presentation.shared?.externalId ?? '');
157
157
  addColumn(headers, row, HEADER_SLIDE_CLM_CONTENT, VEEVA_YES);
158
158
  addColumn(headers, row, HEADER_PRESENTATION, config.presentation.externalId);
@@ -180,7 +180,7 @@ function createSharedRow(headers, config, additional) {
180
180
  addColumn(headers, row, HEADER_SLIDE_PRODUCT, config.presentation.product);
181
181
  addColumn(headers, row, HEADER_SLIDE_COUNTRY, config.presentation.country);
182
182
  addColumn(headers, row, HEADER_SLIDE_MEDIA_TYPE, shared.mediaType ?? VEEVA_MEDIATYPE_HTML);
183
- addColumn(headers, row, HEADER_SLIDE_FILE_NAME, pathToSlideZip(shared.path ?? 'shared'));
183
+ addColumn(headers, row, HEADER_SLIDE_FILE_NAME, pathToSlideZip(shared.zipFileName ?? shared.path ?? 'shared'));
184
184
  addColumn(headers, row, HEADER_SLIDE_CLM_CONTENT, VEEVA_YES);
185
185
  addColumn(headers, row, HEADER_SLIDE_CRM_SHARED, VEEVA_YES);
186
186
  addColumn(headers, row, HEADER_LIFECYCLE, VEEVA_LIFECYCLE_CRM_CONTENT);
@@ -1,5 +1,7 @@
1
+ import { IVeevaConfig } from '../types';
1
2
  interface Params {
3
+ config: IVeevaConfig;
2
4
  deployDir: string;
3
5
  }
4
- export default function ({ deployDir }: Params): Promise<void>;
6
+ export default function ({ config, deployDir }: Params): Promise<void>;
5
7
  export {};
@@ -3,14 +3,18 @@ import { join } from 'node:path';
3
3
  import Zip from 'adm-zip';
4
4
  import logger from '../lib/logger.js';
5
5
  import { pathToSlideZip } from '../lib/utils.js';
6
- export default async function ({ deployDir }) {
6
+ export default async function ({ config, deployDir }) {
7
7
  logger.info('Creating ZIP files');
8
8
  const dirs = await readdir(deployDir);
9
9
  for (const dir of dirs) {
10
10
  const dirPath = join(deployDir, dir);
11
11
  const zip = new Zip();
12
12
  zip.addLocalFolder(dirPath);
13
- await zip.writeZipPromise(join(deployDir, pathToSlideZip(dir)));
13
+ await zip.writeZipPromise(join(deployDir, getZipFileName(config, dir)));
14
14
  await rm(dirPath, { recursive: true });
15
15
  }
16
16
  }
17
+ function getZipFileName(config, dir) {
18
+ const zipFileName = dir === 'shared' ? config.presentation.shared?.zipFileName : config.presentation.slides.find(s => s.zipFileName === dir)?.zipFileName;
19
+ return pathToSlideZip(zipFileName ?? dir);
20
+ }
@@ -1,7 +1,9 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { mkdir, rm, readdir } from 'node:fs/promises';
3
3
  import { join } from "node:path";
4
+ import { fileURLToPath } from 'node:url';
4
5
  import { preview } from "astro";
6
+ import { validateConfig } from "astro/config";
5
7
  import parseArgv from '../lib/parse-argv.js';
6
8
  import copyFiles from './copy-files.js';
7
9
  import generateThumbs from './generate-thumbs.js';
@@ -15,10 +17,12 @@ const argv = parseArgv({
15
17
  export default async function ({ root, config, env, csvOnly = false, serverPort = 4320 }) {
16
18
  const rootDir = root ?? process.cwd();
17
19
  // we could import the astro config and see if the outDir was set but right now it's not necessary
18
- const astroDistDir = join(rootDir, 'dist');
20
+ const astroConfig = await getAstroConfig(root);
21
+ const outDir = astroConfig ? fileURLToPath(astroConfig.outDir).replace(root, '') : 'dist';
22
+ const astroDistDir = join(rootDir, outDir);
19
23
  const buildName = `${env}${config.presentation.isTraining === true ? '-training' : ''}`;
20
24
  const buildType = argv.mode ?? buildName;
21
- const deployDir = join(rootDir, 'deploy', buildType);
25
+ const deployDir = join(rootDir, outDir !== 'deploy' ? 'deploy' : 'deploy-veeva', buildType);
22
26
  if (existsSync(deployDir)) {
23
27
  const items = await readdir(deployDir);
24
28
  for (const item of items) {
@@ -44,7 +48,28 @@ export default async function ({ root, config, env, csvOnly = false, serverPort
44
48
  root
45
49
  });
46
50
  await server.stop();
47
- await createZips({ deployDir });
51
+ await createZips({ config, deployDir });
48
52
  }
49
53
  await createCsv({ config, deployDir, buildName, fieldsOnly: csvOnly });
50
54
  }
55
+ async function getAstroConfig(root) {
56
+ let config = undefined;
57
+ try {
58
+ config = await validateConfig({}, root, 'build');
59
+ }
60
+ catch (err) {
61
+ console.error('Failed to parse Astro config');
62
+ }
63
+ return config;
64
+ // console.log('Searching for Astro config');
65
+ // const files = await readdir(root, { withFileTypes: true });
66
+ // const configFile = files.find((f) => {
67
+ // return f.isFile() || /^astro\.config\.(?:js|cjs|mjs|ts)$/i.test(f.name);
68
+ // });
69
+ // if (!configFile) {
70
+ // throw new Error('Could not locate Astro config file');
71
+ // }
72
+ // const { default: astroConfig } = await import(pathToFileURL(join(root, configFile.name)).toString());
73
+ // console.log(astroConfig);
74
+ // return astroConfig;
75
+ }
@@ -1,7 +1,8 @@
1
1
  import { writeFile } from "node:fs/promises";
2
- import { join } from "node:path";
2
+ import { extname, join } from "node:path";
3
3
  import puppeteer from "puppeteer";
4
4
  import sharp from 'sharp';
5
+ import { pdf } from "pdf-to-img";
5
6
  import logger from '../lib/logger.js';
6
7
  export default async function ({ config, root, deployDir, server }) {
7
8
  logger.info('Generating thumbnails');
@@ -18,21 +19,41 @@ export default async function ({ config, root, deployDir, server }) {
18
19
  });
19
20
  try {
20
21
  for (const slide of config.presentation.slides) {
22
+ if (slide.thumbnails === false)
23
+ continue;
24
+ // this shouldn't ever be the case after initThumbSettings runs but it keeps typescript happy
25
+ if (!slide.thumbnails || !slide.thumbnails.settings)
26
+ continue;
21
27
  for (const settings of slide.thumbnails.settings) {
22
- logger.info(`Generating /${settings.path ?? slide.path}/`);
23
- const url = `${server.protocol ?? 'http'}://${server.host}:${server.port}/${settings.path ?? slide.path}/${settings.queryStringParams ? `?${objToQS(settings.queryStringParams)}` : ''}`;
24
- await page.goto(url, {
25
- waitUntil: 'networkidle0'
26
- });
27
- if (settings.delay && settings.delay > 0) {
28
- logger.info(`Delaying thumbnail generation for ${settings.delay}ms`);
29
- await sleep(settings.delay);
28
+ const settingsPath = settings.path ?? slide.path;
29
+ const urlPath = `${settingsPath.startsWith('/') ? '' : '/'}${settingsPath}${extname(settingsPath) !== '' ? '' : '/'}`;
30
+ logger.info(`Generating ${urlPath}`);
31
+ let screenshot;
32
+ switch (slide.mediaType) {
33
+ case 'PDF': {
34
+ await using document = await pdf(join(deployDir, settingsPath), { scale: 2 });
35
+ screenshot = await document.getPage(1);
36
+ break;
37
+ }
38
+ case 'HTML':
39
+ default: {
40
+ const url = `${server.protocol ?? 'http'}://${server.host}:${server.port}${urlPath}${settings.queryStringParams ? `?${objToQS(settings.queryStringParams)}` : ''}`;
41
+ await page.goto(url, {
42
+ waitUntil: 'networkidle0'
43
+ });
44
+ if (settings.delay && settings.delay > 0) {
45
+ logger.info(`Delaying thumbnail generation for ${settings.delay}ms`);
46
+ await sleep(settings.delay);
47
+ }
48
+ screenshot = await page.screenshot();
49
+ break;
50
+ }
30
51
  }
31
- let screenshot = await page.screenshot();
32
52
  let thumb = await (sharp(screenshot)
33
53
  .resize({
34
54
  width: settings.width,
35
- height: settings.height
55
+ height: settings.height,
56
+ fit: 'inside'
36
57
  })
37
58
  .png({
38
59
  palette: settings.palette ?? true,
@@ -165,6 +186,8 @@ function initThumbSettings(config) {
165
186
  // }
166
187
  // }
167
188
  for (const slide of config.presentation.slides) {
189
+ if (slide.thumbnails === false)
190
+ continue;
168
191
  if (!slide.thumbnails) {
169
192
  slide.thumbnails = {};
170
193
  }
@@ -200,6 +223,9 @@ function initThumbSettings(config) {
200
223
  }
201
224
  }
202
225
  }
226
+ for (let i = 0; i < slide.thumbnails.settings.length; i++) {
227
+ slide.thumbnails.settings[i].width ??= config.thumbnails[0]?.width ?? DEFAULT_WIDTH;
228
+ }
203
229
  }
204
230
  }
205
231
  function sleep(timeout = 0) {
@@ -1,4 +1,5 @@
1
1
  import { readdir, readFile, writeFile } from 'node:fs/promises';
2
+ import { existsSync } from 'node:fs';
2
3
  import { join, extname } from 'node:path';
3
4
  import logger from '../lib/logger.js';
4
5
  export default async function ({ deployDir }) {
@@ -29,13 +30,19 @@ async function handleShared(deployDir, dir) {
29
30
  }
30
31
  }
31
32
  async function handleHTML(deployDir, dir) {
32
- await updateSharedPath(join(deployDir, dir, 'index.html'));
33
+ const index = join(deployDir, dir, 'index.html');
34
+ if (!existsSync(index))
35
+ return;
36
+ await updateSharedPath(index);
33
37
  }
34
38
  async function updateSharedPath(filepath) {
35
39
  const str = await readFile(filepath, 'utf8');
36
40
  let shared = './shared/';
37
- if (extname(filepath) === '.css') { // relative paths inside a css
38
- shared = './';
41
+ switch (extname(filepath)) {
42
+ case '.css':
43
+ case '.js':
44
+ shared = './';
45
+ break;
39
46
  }
40
47
  await writeFile(filepath, str.replace(/(?:\.*\/)*shared\//g, shared));
41
48
  }
@@ -37,12 +37,14 @@ export interface ISlideThumbnailSettings {
37
37
  export interface IVeevaConfigSlide extends IVeevaClmSlide {
38
38
  path: string;
39
39
  enableActions?: Actions[];
40
- thumbnails?: ISlideThumbnailSettings;
40
+ thumbnails?: false | ISlideThumbnailSettings;
41
+ zipFileName?: string;
41
42
  }
42
43
 
43
44
  export interface IVeevaConfigPresentation extends IVeevaClmPresentation {
44
45
  shared?: IVeevaClmSharedResource & {
45
46
  path?: string;
47
+ zipFileName?: string;
46
48
  },
47
49
  slides: IVeevaConfigSlide[]
48
50
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inizioevoke/veeva-astroclm-core",
3
- "version": "1.3.9",
3
+ "version": "1.4.1",
4
4
  "description": "",
5
5
  "license": "ISC",
6
6
  "author": "",
@@ -34,6 +34,7 @@
34
34
  "dependencies": {
35
35
  "@clack/prompts": "^1.4.0",
36
36
  "adm-zip": "^0.5.17",
37
+ "pdf-to-img": "^7.0.0",
37
38
  "puppeteer": "^24.43.1",
38
39
  "sharp": "^0.34.5",
39
40
  "yaml": "^2.9.0"
@@ -44,6 +44,7 @@ export default function ({ veevaEnv = 'client', isTraining = false }: IVeevaConf
44
44
 
45
45
  shared: {
46
46
  externalId: formatExtId('SHARED'),
47
+ // zipFileName: 'shared.zip',
47
48
  ...binderFields
48
49
  },
49
50
 
package/src/lib/utils.ts CHANGED
@@ -5,7 +5,7 @@ export function formatExtId(...ids: string[]) {
5
5
  }
6
6
 
7
7
  export function pathToSlideZip(path: string) {
8
- return `${flattenPath(path, { slashes: false })}.zip`;
8
+ return `${flattenPath(path, { slashes: false }).replace(/\.zip$/i,'')}.zip`;
9
9
  }
10
10
 
11
11
  export function flattenPath(path: string, { slashes = true }: { slashes?: boolean } = {}) {
@@ -229,7 +229,7 @@ function createSlideRow(headers: string[], config: IVeevaConfig, slide: IVeevaCo
229
229
  disable.delete('Reactions');
230
230
  addColumn(headers, row, HEADER_SLIDE_DISABLE_ACTIONS, [...disable].join(','));
231
231
 
232
- addColumn(headers, row, HEADER_SLIDE_FILE_NAME, pathToSlideZip(slide.path));
232
+ addColumn(headers, row, HEADER_SLIDE_FILE_NAME, pathToSlideZip(slide.zipFileName ?? slide.path));
233
233
  addColumn(headers, row, HEADER_SLIDE_RELATED_SHARED, config.presentation.shared?.externalId ?? '');
234
234
  addColumn(headers, row, HEADER_SLIDE_CLM_CONTENT, VEEVA_YES);
235
235
  addColumn(headers, row, HEADER_PRESENTATION, config.presentation.externalId);
@@ -260,7 +260,7 @@ function createSharedRow(headers: string[], config: IVeevaConfig, additional?: A
260
260
  addColumn(headers, row, HEADER_SLIDE_PRODUCT, config.presentation.product);
261
261
  addColumn(headers, row, HEADER_SLIDE_COUNTRY, config.presentation.country);
262
262
  addColumn(headers, row, HEADER_SLIDE_MEDIA_TYPE, shared.mediaType ?? VEEVA_MEDIATYPE_HTML);
263
- addColumn(headers, row, HEADER_SLIDE_FILE_NAME, pathToSlideZip(shared.path ?? 'shared'));
263
+ addColumn(headers, row, HEADER_SLIDE_FILE_NAME, pathToSlideZip(shared.zipFileName ?? shared.path ?? 'shared'));
264
264
  addColumn(headers, row, HEADER_SLIDE_CLM_CONTENT, VEEVA_YES);
265
265
  addColumn(headers, row, HEADER_SLIDE_CRM_SHARED, VEEVA_YES);
266
266
  addColumn(headers, row, HEADER_LIFECYCLE, VEEVA_LIFECYCLE_CRM_CONTENT);
@@ -3,11 +3,13 @@ import { join } from 'node:path';
3
3
  import Zip from 'adm-zip';
4
4
  import logger from '../lib/logger.js';
5
5
  import { pathToSlideZip } from '../lib/utils.js';
6
+ import { IVeevaConfig } from '../types';
6
7
 
7
8
  interface Params {
9
+ config: IVeevaConfig;
8
10
  deployDir: string;
9
11
  }
10
- export default async function({ deployDir }: Params) {
12
+ export default async function({ config, deployDir }: Params) {
11
13
  logger.info('Creating ZIP files');
12
14
  const dirs = await readdir(deployDir);
13
15
 
@@ -15,7 +17,13 @@ export default async function({ deployDir }: Params) {
15
17
  const dirPath = join(deployDir, dir);
16
18
  const zip = new Zip();
17
19
  zip.addLocalFolder(dirPath);
18
- await zip.writeZipPromise(join(deployDir, pathToSlideZip(dir)));
20
+ await zip.writeZipPromise(join(deployDir, getZipFileName(config, dir)));
19
21
  await rm(dirPath, { recursive: true });
20
22
  }
23
+ }
24
+
25
+ function getZipFileName(config: IVeevaConfig, dir: string) {
26
+ const zipFileName = dir === 'shared' ? config.presentation.shared?.zipFileName : config.presentation.slides.find(s => s.zipFileName === dir)?.zipFileName;
27
+
28
+ return pathToSlideZip(zipFileName ?? dir);
21
29
  }
@@ -1,7 +1,9 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { mkdir, rm, readdir } from 'node:fs/promises';
3
- import { join, resolve as resolvePath } from "node:path"
4
- import { preview } from "astro";
3
+ import { join, resolve as resolvePath } from "node:path";
4
+ import { pathToFileURL, fileURLToPath } from 'node:url';
5
+ import { AstroConfig, preview } from "astro";
6
+ import { validateConfig } from "astro/config";
5
7
  import { IVeevaConfig } from '../types';
6
8
  import loadEnv from '../lib/env.js';
7
9
  import logger from '../lib/logger.js';
@@ -29,10 +31,12 @@ interface Opts {
29
31
  export default async function({ root, config, env, csvOnly = false, serverPort = 4320 }: Opts) {
30
32
  const rootDir = root ?? process.cwd();
31
33
  // we could import the astro config and see if the outDir was set but right now it's not necessary
32
- const astroDistDir = join(rootDir, 'dist');
34
+ const astroConfig = await getAstroConfig(root);
35
+ const outDir = astroConfig ? fileURLToPath(astroConfig.outDir).replace(root, '') : 'dist';
36
+ const astroDistDir = join(rootDir, outDir);
33
37
  const buildName = `${env}${config.presentation.isTraining === true ? '-training' : ''}`;
34
38
  const buildType = argv.mode ?? buildName;
35
- const deployDir = join(rootDir, 'deploy', buildType);
39
+ const deployDir = join(rootDir, outDir !== 'deploy' ? 'deploy' : 'deploy-veeva', buildType);
36
40
 
37
41
  if (existsSync(deployDir)) {
38
42
  const items = await readdir(deployDir);
@@ -63,11 +67,31 @@ export default async function({ root, config, env, csvOnly = false, serverPort =
63
67
 
64
68
  await server.stop();
65
69
 
66
- await createZips({ deployDir });
70
+ await createZips({ config, deployDir });
67
71
  }
68
72
 
69
73
  await createCsv({ config, deployDir, buildName, fieldsOnly: csvOnly })
70
74
  }
71
75
 
72
76
 
77
+ async function getAstroConfig(root: string) {
78
+ let config: AstroConfig | undefined = undefined;
79
+ try {
80
+ config = await validateConfig({}, root, 'build');
81
+ } catch (err) {
82
+ console.error('Failed to parse Astro config')
83
+ }
84
+ return config;
85
+ // console.log('Searching for Astro config');
86
+ // const files = await readdir(root, { withFileTypes: true });
87
+ // const configFile = files.find((f) => {
88
+ // return f.isFile() || /^astro\.config\.(?:js|cjs|mjs|ts)$/i.test(f.name);
89
+ // });
90
+ // if (!configFile) {
91
+ // throw new Error('Could not locate Astro config file');
92
+ // }
93
+ // const { default: astroConfig } = await import(pathToFileURL(join(root, configFile.name)).toString());
94
+ // console.log(astroConfig);
95
+ // return astroConfig;
96
+ }
73
97
 
@@ -1,7 +1,8 @@
1
1
  import { writeFile } from "node:fs/promises";
2
- import { join } from "node:path";
2
+ import { extname, join } from "node:path";
3
3
  import puppeteer, { type Viewport } from "puppeteer";
4
4
  import sharp from 'sharp';
5
+ import { pdf } from "pdf-to-img";
5
6
 
6
7
  import logger from '../lib/logger.js';
7
8
  import type { IVeevaConfig, IThumbnailSettings, IScreenshotSettings } from "../types";
@@ -34,23 +35,48 @@ export default async function({ config, root, deployDir, server }: Params) {
34
35
 
35
36
  try {
36
37
  for (const slide of config.presentation.slides) {
37
- for (const settings of slide.thumbnails!.settings!) {
38
- logger.info(`Generating /${settings.path ?? slide.path}/`);
39
- const url = `${server.protocol ?? 'http'}://${server.host}:${server.port}/${settings.path ?? slide.path}/${settings.queryStringParams ? `?${objToQS(settings.queryStringParams)}` : ''}`
38
+ if (slide.thumbnails === false) continue;
40
39
 
41
- await page.goto(url, {
42
- waitUntil: 'networkidle0'
43
- });
44
- if (settings.delay && settings.delay > 0) {
45
- logger.info(`Delaying thumbnail generation for ${settings.delay}ms`);
46
- await sleep(settings.delay);
40
+ // this shouldn't ever be the case after initThumbSettings runs but it keeps typescript happy
41
+ if (!slide.thumbnails || !slide.thumbnails.settings) continue;
42
+
43
+
44
+
45
+
46
+ for (const settings of slide.thumbnails.settings) {
47
+ const settingsPath = settings.path ?? slide.path;
48
+ const urlPath = `${settingsPath.startsWith('/') ? '' : '/'}${settingsPath}${extname(settingsPath) !== '' ? '' : '/'}`;
49
+ logger.info(`Generating ${urlPath}`);
50
+
51
+ let screenshot: Buffer | Uint8Array;
52
+ switch (slide.mediaType) {
53
+ case 'PDF': {
54
+ await using document = await pdf(join(deployDir, settingsPath), { scale: 2 });
55
+ screenshot = await document.getPage(1);
56
+ break;
57
+ }
58
+ case 'HTML':
59
+ default: {
60
+ const url = `${server.protocol ?? 'http'}://${server.host}:${server.port}${urlPath}${settings.queryStringParams ? `?${objToQS(settings.queryStringParams)}` : ''}`
61
+
62
+ await page.goto(url, {
63
+ waitUntil: 'networkidle0'
64
+ });
65
+ if (settings.delay && settings.delay > 0) {
66
+ logger.info(`Delaying thumbnail generation for ${settings.delay}ms`);
67
+ await sleep(settings.delay);
68
+ }
69
+ screenshot = await page.screenshot();
70
+ break;
71
+ }
47
72
  }
48
- let screenshot = await page.screenshot();
73
+
49
74
  let thumb = await (
50
75
  sharp(screenshot)
51
76
  .resize({
52
77
  width: settings.width,
53
- height: settings.height
78
+ height: settings.height,
79
+ fit: 'inside'
54
80
  })
55
81
  .png({
56
82
  palette: settings.palette ?? true,
@@ -189,6 +215,8 @@ function initThumbSettings(config: IVeevaConfig) {
189
215
  // }
190
216
 
191
217
  for (const slide of config.presentation.slides) {
218
+ if (slide.thumbnails === false) continue;
219
+
192
220
  if (!slide.thumbnails) {
193
221
  slide.thumbnails = {};
194
222
  }
@@ -224,6 +252,10 @@ function initThumbSettings(config: IVeevaConfig) {
224
252
  }
225
253
  }
226
254
  }
255
+ for (let i=0; i<slide.thumbnails.settings.length; i++) {
256
+ slide.thumbnails.settings[i].width ??= config.thumbnails[0]?.width ?? DEFAULT_WIDTH;
257
+
258
+ }
227
259
  }
228
260
 
229
261
  }
@@ -1,4 +1,5 @@
1
1
  import { readdir, readFile, writeFile } from 'node:fs/promises';
2
+ import { existsSync } from 'node:fs';
2
3
  import { join, extname } from 'node:path';
3
4
 
4
5
  import logger from '../lib/logger.js';
@@ -36,14 +37,19 @@ async function handleShared(deployDir: string, dir: string) {
36
37
  }
37
38
 
38
39
  async function handleHTML(deployDir: string, dir: string) {
39
- await updateSharedPath(join(deployDir, dir, 'index.html'));
40
+ const index = join(deployDir, dir, 'index.html');
41
+ if (!existsSync(index)) return;
42
+ await updateSharedPath(index);
40
43
  }
41
44
 
42
45
  async function updateSharedPath(filepath: string) {
43
46
  const str = await readFile(filepath, 'utf8');
44
47
  let shared = './shared/';
45
- if (extname(filepath) === '.css') { // relative paths inside a css
46
- shared = './';
48
+ switch (extname(filepath)) {
49
+ case '.css':
50
+ case '.js':
51
+ shared = './';
52
+ break;
47
53
  }
48
54
  await writeFile(filepath, str.replace(/(?:\.*\/)*shared\//g, shared));
49
55
  }
@@ -37,12 +37,14 @@ export interface ISlideThumbnailSettings {
37
37
  export interface IVeevaConfigSlide extends IVeevaClmSlide {
38
38
  path: string;
39
39
  enableActions?: Actions[];
40
- thumbnails?: ISlideThumbnailSettings;
40
+ thumbnails?: false | ISlideThumbnailSettings;
41
+ zipFileName?: string;
41
42
  }
42
43
 
43
44
  export interface IVeevaConfigPresentation extends IVeevaClmPresentation {
44
45
  shared?: IVeevaClmSharedResource & {
45
46
  path?: string;
47
+ zipFileName?: string;
46
48
  },
47
49
  slides: IVeevaConfigSlide[]
48
50
  }